I have a console application which is runs as a Windows Task.
One of the actions done is to add a computer to an AD group.
The code below does that:
public static bool AddToGroup(string machineName, string groupName)
{
using (
var context = new PrincipalContext(
ContextType.Domain,
Helper.GetAppSetting("Domain"),
Helper.GetAppSetting("ServiceAccountLogonName"),
Helper.GetAppSetting("ServiceAccountPassword")))
{
using (var group = GroupPrincipal.FindByIdentity(context, groupName))
{
using (var computer = ComputerPrincipal.FindByIdentity(context, machineName))
{
if (group == null || computer == null)
{
return false;
}
group.Members.Add(computer);
group.Save();
return true;
}
}
}
}
This function and the task have been running without an issue.
This task failed once with the following error message: Exception of
type 'System.OutOfMemoryException' was thrown.
Is it because of the of the machine or group which is being searched for?
Is there an issue in the code as it is which makes it susceptible to OutOfMemoryExceptions?
I found the issue on the computer.
The paging file on the drive where the executable was running was set to None.
I am open to hear other possible issues here
Related
I have read the relevant Stack Overflow questions and tried out the following code:
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (null != identity)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
return false;
It does not return true even though I have manually confirmed that the current user is a member of the local built-in Administrators group.
What am I missing ?
Thanks.
Just found other way to check if user is admin, not running application as admin:
private static bool IsAdmin()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (identity != null)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
List<Claim> list = new List<Claim>(principal.UserClaims);
Claim c = list.Find(p => p.Value.Contains("S-1-5-32-544"));
if (c != null)
return true;
}
return false;
}
Credit to this answer, but code is corrected a bit.
The code you have above seemed to only work if running as an administrator, however you can query to see if the user belongs to the local administrators group (without running as an administrator) by doing something like the code below. Note, however, that the group name is hard-coded, so I guess you would have some localization work to do if you want to run it on operating systems of different languages.
using (var pc = new PrincipalContext(ContextType.Domain, Environment.UserDomainName))
{
using (var up = UserPrincipal.FindByIdentity(pc, WindowsIdentity.GetCurrent().Name))
{
return up.GetAuthorizationGroups().Any(group => group.Name == "Administrators");
}
}
Note that you can also get a list of ALL the groups the user is a member of by doing this inside the second using block:
var allGroups = up.GetAuthorizationGroups();
But this will be much slower depending on how many groups they're a member of. For example, I'm in 638 groups and it takes 15 seconds when I run it.
I am getting an win32 exception that I need help debugging. I have a SharePoint page that has a custom Web Part that when I browse to that page, I get the following message:
In w3wp.dmp the assembly instruction at ntdll!RtlReportCriticalFailure+62 in
C:\Windows\System32\ntdll.dll from Microsoft Corporation has caused an unknown
exception (0xc0000374) on thread 43
Unhandled exception at 0xHEX in w3wp.exe: 0xc0000374:
A heap has been corrupted
I don't really know where to begin debugging this issue and searching the internet has yielded very little results.
I have saved the DMP file and I've loaded it into the Debug Diagnostic Tool for analysis. I don't know what parts are most important, so if someone could tell me which sections I should post, I will post them.
Where I think the problem is:
I have narrowed down the location of where the problem might be. If I don't call these methods, I don't get the error. Is there something wrong with the way I'm accessing the TermStore?
private void GetTerms()
{
using (SPSite site = new SPSite(PhaseListUrl.Value))
{
var session = new TaxonomySession(site);
if (session.TermStores == null || session.TermStores.Count == 0) return;
OfficeDropdown.Items.Clear();
OfficeDropdown.Items.Add("");
var termStore = session
.TermStores
.FirstOrDefault(ts => ts.Groups.Any(g => g.Name == TaxonomyName.Value));
try
{
var group = termStore.Groups[TaxonomyName.Value];
foreach (var ts in group.TermSets)
{
if (!ts.IsAvailableForTagging) continue;
AddTerms(site, termStore, ts.Id, ts.Terms);
}
}
catch (NullReferenceException) { OfficeDropdown.Items.Add("ERROR: Check your Org Term Store Name"); }
catch (ArgumentOutOfRangeException) { OfficeDropdown.Items.Add("ERROR: Check your Org Term Store Name"); }
}
}
private void AddTerms(SPSite site, TermStore termStore, Guid tsId, TermCollection coll)
{
foreach (var t in coll)
{
if (t.IsAvailableForTagging)
{
var wssIds = TaxonomyField.GetWssIdsOfTerm(site, termStore.Id, tsId, t.Id, true, 1000);
if (wssIds.Length != 0)
{
var id = wssIds[0];
var validatedString = string.Format("{0};#{1}{2}{3}", id, t.Name, TaxonomyField.TaxonomyGuidLabelDelimiter, t.Id);
OfficeDropdown.Items.Add(new ListItem(t.Name, validatedString));
}
else
{
int id = -1;
var value = new TaxonomyFieldValue(SPContext.Current.Web.AvailableFields[new Guid("{a64c1f69-c0d6-421a-8c8a-9ef8f459d7a2}")]);
value.TermGuid = t.Id.ToString();
var dummy = value.ValidatedString;
if (dummy != null)
id = value.WssId;
var validatedString = string.Format("{0};#{1}{2}{3}", id, t.Name, TaxonomyField.TaxonomyGuidLabelDelimiter, t.Id);
OfficeDropdown.Items.Add(new ListItem(t.Name, validatedString));
}
}
AddTerms(site, termStore, tsId, t.Terms);
}
}
UPDATE:
The bug was in the TaxonomyField. I was accessing terms cross site collection and the IDs hadn't been generated. Also look out for TermStore IDs not being the same cross site collection (and know where you are calling from and where you are calling to!)
Here are some suggestions.
Pull up ULS Log Viewer and study the logs, if you hit the page and get that error, it should be logged there.
Check the application event logs on the web front-end server(s).
Have you also tried attaching to the W3P.exe process and debug the solution? (Assuming it's not the production environment of course).
I am creating accounts and setting properties on them using System.DirectoryServices.AccountManagement in .NET 4.5. One of the requirements is that the group membership (including Primary Group) be copied from a template account. The code includes the following:
foreach (var group in userPrincipal.GetGroups()) {
var groupPrincipal = (GroupPrincipal) #group;
if (groupPrincipal.Sid != templatePrimaryGroup.Sid) {
groupPrincipal.Members.Remove(userPrincipal);
groupPrincipal.Save();
}
}
This works about 90% of the time. The rest of the time, it fails with:
System.DirectoryServices.DirectoryServicesCOMException was
unhandled HResult=-2147016656 Message=There is no such object on
the server.
Source=System.DirectoryServices ErrorCode=-2147016656
ExtendedError=8333 ExtendedErrorMessage=0000208D: NameErr:
DSID-03100213, problem 2001 (NO_OBJECT), data 0, best match of:
'OU=Whatever,DC=domain,DC=local`
on the GetGroups call. My guess is that there is a race condition of some sort with the user not being fully created before I next go to access it. I know from diagnostic logging that I am going against the same domain controller each time (it's using the same PrincipalContext so that matches my expectation) so it's not a replication issue.
Is my guess accurate? Is there a good way to handle this? I could just throw in a Sleep but that seems like a cop-out at best and fragile at worst. So what is the right thing to do?
Try something like:
int maxWait = 120;
int cnt = 0;
bool usable = false;
while (usable == false && cnt < maxWait)
{
try
{
foreach (var group in userPrincipal.GetGroups())
{
var groupPrincipal = (GroupPrincipal)#group;
if (groupPrincipal.Sid != templatePrimaryGroup.Sid)
{
groupPrincipal.Members.Remove(userPrincipal);
groupPrincipal.Save();
}
}
usable = true;
break;
}
catch
{
System.Threading.Thread.Sleep(500);
}
}
if (usable)
//All okay
;
else
//Do something
;
This way you can try for "a while". If it works good, if not do something like log an error, so you can run a fix-it script later.
I have the following code that takes a username and a password and then creates the user on the machine and adds them to two specific groups. When I get to the group part it is really slow and I have no idea why. My last run according to my logs file says that adding the user to the Users group took 7 minutes, but the IIS_IUSRS was super fast.
below is my initial code that calls the methods that do the real work. I have tried using a task to help speed up the process of checking for groups, but it still runs super slow.
public void Apply(Section.User.User user, Action<string> status)
{
#region Sanity Checks
if (user == null)
{
throw new ArgumentNullException("user");
}
if (status == null)
{
throw new ArgumentNullException("status");
}
#endregion
_logger.Debug(string.Format("Starting to apply the user with name {0}", user.UserName));
status(string.Format("Applying User {0} to the system.", user.UserName));
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine))
{
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(pc, user.UserName);
try
{
_logger.Debug("Checking if user already exists");
if (userPrincipal == null)
{
userPrincipal = CreateNewUser(user, pc);
}
_logger.Debug("Setting user password and applying to the system.");
userPrincipal.SetPassword(user.UserPassword);
userPrincipal.Save();
Task<PrincipalSearchResult<Principal>> groups =
Task<PrincipalSearchResult<Principal>>.Factory.StartNew(userPrincipal.GetGroups);
_logger.Debug("Adding user to the groups.");
AddUserToGroups(pc, userPrincipal, groups, user.UserType.Equals(UserType.WorkerProcess.ToString()) ? "Administrators" : "Users", "IIS_IUSRS");
AddCurrentUser(user);
}
finally
{
if (userPrincipal != null)
{
userPrincipal.Dispose();
}
}
}
}
This is my private method I use to create the user if it doesn't exist.
private UserPrincipal CreateNewUser(Section.User.User user, PrincipalContext principal)
{
_logger.Debug("User did not exist creating now.");
UserPrincipal newUser = new UserPrincipal(principal)
{
Name = user.UserName,
Description = user.UserDescription,
UserCannotChangePassword = false,
PasswordNeverExpires = true,
PasswordNotRequired = false
};
_logger.Debug("User created.");
return newUser;
}
Below is the logic for the groups. I have made a comment above the offending code that I get hung on whenever I walk through with the debugger. Also the debug log entry is always the last one I get before the hang as well.
private void AddUserToGroups(PrincipalContext principal, UserPrincipal user, Task<PrincipalSearchResult<Principal>> userGroups, params string[] groups)
{
groups.AsParallel().ForAll(s =>
{
using (GroupPrincipal gp = GroupPrincipal.FindByIdentity(principal, s))
{
_logger.Debug(string.Format("Checking if user is alread in the group."));
if (gp != null && !userGroups.Result.Contains(gp))
{
_logger.Debug(string.Format("The user was not a member of {0} adding them now.", gp.Name));
//This is the point that the 7 minute hang starts
gp.Members.Add(user);
gp.Save();
_logger.Debug(string.Format("User added to {0}.", gp.Name));
}
}
});
}
Any help with this would be greatly appreciated as this project is expected to release in October, but I can't release with a 7 minute hang when creating a user.
Had the same problem. It seems that
gp.Members.Add( user );
is slow because it first enumerates groups (to get Members) and only then it adds to the collection (which adds another slowdown).
The solution was to have it like:
UserPrincipal user = this is your user;
GroupPrincipal group = this is your group;
// this is fast
using ( DirectoryEntry groupEntry = group.GetUnderlyingObject() as DirectoryEntry )
using ( DirectoryEntry userEntry = user.GetUnderlyingObject() as DirectoryEntry )
{
groupEntry.Invoke( "Add", new object[] { userEntry.Path } );
}
//group.Members.Add(user); // and this is slow!
//group.Save();
Just a tip - creating passwords with SetPassword was also terribly slow for us. The solution was to follow the approach from "The .NET Developer's Guide to Directory Services Programming" where they use a low level password setting using LdapConnection from System.DirectoryServices.Protocols.
The last bottleneck we've discovered was caused by the User.GetGroups() method.
Anyway, drop a note if the code for adding users to groups makes a difference for you. Also note that you don't really need to perform this in parallel - I understand that this was your approach to speed up the code but you don't really need this.
I'm using the IronCow managed API for RememberTheMilk (http://ironcow.codeplex.com/) and I'm trying to remove tasks using my program. I've already logged in and downloaded the tasks list, but when I later try to remove one I get the following exception:
[IronCow.RtmException] = {"User not logged in / Insufficient permissions"}
I'm removing tasks using this code (rtm is my logged in RTM object, myTask is the Task object I'm looking to delete)
TaskListCollection tlc = rtm.TaskLists;
foreach (TaskList list in tlc)
{
TaskListTaskCollection taskListsTasks = list.Tasks;
foreach (Task task in taskListTasks)
{
if (!(task.IsDeleted || task.IsCompleted) && task.Name == myTask.Name)
{
list.Tasks.Remove(task);
}
}
}
the line it errors on is list.Tasks.Remove
Discovered it was a permissions problem due to permissions being stored from an old version of the app that didn't need to delete