Active Directory group lookup function failing - asp.net

Help! I've been trying to write a function that will confirm a user's membership in an Active Directory group, and while it works if the member happens to be in the group, it throws an exception if the user is not.
Here is the function:
private bool IsUserMemberOfGroup(string user, string group)
{
using (var ctx = new PrincipalContext(ContextType.Domain))
using (var groupPrincipal = GroupPrincipal.FindByIdentity(ctx, group))
using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user))
{
if (groupPrincipal == null)
{
return false;
}
else
{
return userPrincipal.IsMemberOf(groupPrincipal);
}
}
}
And here is the YSOD:
Server Error in '/' Application.
Unknown error (0x80005000)
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.Runtime.InteropServices.COMException: Unknown error (0x80005000)
Source Error:
Line 34: else
Line 35: {
Line 36: return userPrincipal.IsMemberOf(groupPrincipal);
Line 37: }
Line 38: }
I don't know if it's related, but when I step through the function, groupPrincipal.Members.Count throws an exception of type "System.NullReferenceException", with Count.Base shows an exception with the message "Object reference not set to instance of an object".
What the heck's going on? Why won't a bool named IsMemberOf just return false when someone's not a member?
Thanks,
Daniel

I think you could simplify things a bit:
private bool IsUserMemberOfGroup(string user, string group)
{
using (var ctx = new PrincipalContext(ContextType.Domain))
using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user))
{
PrincipalSearchResult<Principal> result = userPrincipal.GetGroups();
GroupPrincipal groupPrincipal =
result.Where(g => g.SamAccountName == groupName).FirstOrDefault();
return (groupPrincipal != null);
}
}
The userPrincipal.GetGroups() will give you a definitive list of all group memberships (including primary group and nested group memberships) for that user; then search that list for the group you're interested in, e.g. by samACcountName or some other property.
If you find the group you're looking for in the PrincipalSearchResult<Principal> returned by GetGroups(), then your user is a member of that group.
You can save yourself at least one "FindByIdentity" call with this.

A minor modification top the code from marc_s, I have:
using (var ctx = new PrincipalContext(ContextType.Domain))
using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user))
using (var groupPrincipal = GroupPrincipal.FindByIdentity(ctx, group))
{
if (userPrincipal == null) return false;
if (groupPrincipal == null) return false;
PrincipalSearchResult<Principal> result = userPrincipal.GetGroups();
Principal grp = result.Where(g => g.Sid == groupPrincipal.Sid).FirstOrDefault();
return (grp != null);
}
Comparing Sid seems to work more reliably than comparing SamAccountName.

We had a bit of a poison group in our setup which caused this to fail for some users but not others. The "FirstOrDefault" logic in the other suggested answers MIGHT have helped us dodge the poison group, but that is no guarantee.
We have two suggestions for others with this problem. First check if you have any groups with a forward slash in the group name (the actual group name, not the "pre-windows 2000" name which will replace it with an underscore). If you can rename all such groups that might fix your problem ... it worked for us.
This workaround also was working for us:
/// <summary>
/// This does a recursive group search for the given user or computer principal.
/// </summary>
public IEnumerable<Principal> GetGroups(Principal principal)
{
return GetGroups(null, principal);
}
private IEnumerable<Principal> GetGroups(HashSet<SecurityIdentifier> ancestorPrincipalSids, Principal parentPrincipal)
{
try
{
//enumerate this here so errors are thrown now and not later
//if the current group name has a forward-slash, I think this
//will always error here
var groups = parentPrincipal.GetGroups().ToArray();
if (groups == null)
{
return Enumerable.Empty<Principal>();
}
//keep track of all ancestors in the group hierarchy to this point
//so that we can handle circular references below
var newAncestors = new HashSet<SecurityIdentifier>(ancestorPrincipalSids ?? Enumerable.Empty<SecurityIdentifier>());
newAncestors.Add(parentPrincipal.Sid);
return groups
.Concat(groups
.Where(g => !newAncestors.Contains(g.Sid)) //handle circular references
.SelectMany(g => GetGroups(newAncestors, g)));
}
catch
{
return Enumerable.Empty<Principal>();
}
}

Related

Visual Studio SDK - IViewTagAggregatorFactoryService.CreateTagAggregator causes an exception

I need to obtain classifications tags for ITextSnapshotLine lines in a current text view.
First, I get the active text view:
public static IWpfTextView GetTextView()
{
var textManager = (IVsTextManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager));
IVsTextView vTextView = null;
var mustHaveFocus = 1;
textManager.GetActiveView(mustHaveFocus, null, out vTextView);
var userData = vTextView as IVsUserData;
if (userData != null)
{
IWpfTextViewHost viewHost;
object holder;
var guidViewHost = DefGuidList.guidIWpfTextViewHost;
userData.GetData(ref guidViewHost, out holder);
viewHost = (IWpfTextViewHost)holder;
var textView = viewHost.TextView;
return textView;
}
return null;
}
Then, I get the collection of ITextViewLine lines from the view and call GetClassificationTags method on each:
GetClassificationTags(new SnapshotSpan(line.Start, line.Length), textView)
The method looks like this:
public IEnumerable<IMappingTagSpan<IClassificationTag>> GetClassificationTags(SnapshotSpan span, ITextView textView)
{
var snapshot = textView.TextSnapshot;
var componentModel = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel));
var exportProvider = componentModel.DefaultExportProvider;
var viewTagAggregatorFactoryService = exportProvider.GetExportedValue<IViewTagAggregatorFactoryService>();
var tagAggregator = viewTagAggregatorFactoryService.CreateTagAggregator<IClassificationTag>(textView);
return tagAggregator.GetTags(span);
}
As a result I have everything classified correctly. However, Visual Studio throws an exception and logs it to the ActivityLog.xml file. This happens only after classifying all the lines for the first time. The information in log file says:
System.InvalidOperationException:
Unexpected false
at Roslyn.Utilities.Contract.ThrowIfFalse(Boolean condition, String message)
at Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.AbstractDiagnosticsTaggerProvider`1.CreateTagger[T](ITextBuffer buffer)
at Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.AbstractDiagnosticsTaggerProvider`1.Microsoft.VisualStudio.Text.Tagging.ITaggerProvider.CreateTagger[T](ITextBuffer buffer)
at Microsoft.VisualStudio.Text.Tagging.Implementation.TagAggregator`1.GatherTaggers(ITextBuffer textBuffer)
I noticed that the exception is not thrown after commenting out the line below:
var tagAggregator = viewTagAggregatorFactoryService.CreateTagAggregator<IClassificationTag>(textView);
Sometimes, there is also this exception in the log file:
System.InvalidOperationException: Unexpected false
at Roslyn.Utilities.Contract.ThrowIfFalse(Boolean condition, String message)
at Microsoft.CodeAnalysis.Editor.Tagging.AbstractAsynchronousTaggerProvider`1.TagSource.GetTagIntervalTreeForBuffer(ITextBuffer buffer)
at Microsoft.CodeAnalysis.Editor.Tagging.AbstractAsynchronousTaggerProvider`1.Tagger.GetTagsWorker(NormalizedSnapshotSpanCollection requestedSpans, Boolean accurate, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.Editor.Tagging.AbstractAsynchronousTaggerProvider`1.Tagger.GetTags(NormalizedSnapshotSpanCollection requestedSpans)
at Microsoft.VisualStudio.Text.Tagging.Implementation.TagAggregator`1.<GetTagsForBuffer>d__38.MoveNext()
My question is: what causes this exception and how can I get rid of it?
The code is open source, you can just take a look. My guess is you're trying that on a background thread.

WF 4 Rehosted Designer - get foreach InArgument Value

After reading this article:
http://blogs.msdn.com/b/tilovell/archive/2009/12/29/the-trouble-with-system-activities-foreach-and-parallelforeach.aspx
I have defined the ForEachFactory as follows:
public class ForEachFactory<T> : IActivityTemplateFactory
{
public Activity Create(DependencyObject target)
{
return new ForEach<T>
{
DisplayName = "ForEachFromFactory",
Body = new ActivityAction<T>
{
Argument = new DelegateInArgument<T>("item")
}
};
}
}
All works well but is it possible to check how that DelegeateInArgument in my case named "item" changes its value ?
So if i have defined an array in the variables section and initialized with
{1, 2, 3} i need a way to check how the "item" takes value 1, 2 and then 3.
To be more accurate, i've added this pic, with a breakpoint on the WriteLine activity inside the foreach. When the execution will stop there, is there a way to find out what the value of item is ?
EDIT 1:
Possible solution in my case:
After struggling a bit more i found one interesting thing:
Adding one of my custom activities in the Body of the ForEach, i am able to get the value of the item like this :
So, my activity derives from : CodeActivity
Inside the protected override String[] Execute(CodeActivityContext context) i am doing this job.To be honest, this solves the thing somehow, but it is doable only in my custom activities. If i would put a WriteLine there for example, i would not be able to retrieve that value.
you can access the DelegeateInArgument of a ForEach activity by inspecting the ModelItem trees parent and checking for DelegeateInArgument's. If you need a specific code example to achieve this I may need a some time to code the example. As it has been a long time since I did this, see my question i asked over on msdn
So basically where your break point is, you can access the variable values as these are defined with n the scope of your activity as 'variables'. However the 'item' variable is actually only accessible from the parent loop activity. So you have to get the model item of the current executing activity and then traverse up the tree to find the parent containing the desired DelegateInArgument.
Can you flesh out exactly what you want to achieve? Is it that when your debugging the workflow in the re-hosted designer you want to display the variable values to the user as they change in the UI?
Edit - added tracking example
So as your wanting to display the variable values during execution of the workflow we need to use tracking to achieve this. In the example your using the author has already implemented some basic tracking. So to achieve the extended variable tracking you want you will need to alter the tracking profile.
Firstly amend the WorkflowDesignerHost.xaml.cs file alter the RunWorkflow method to define the SimulatorTrackingParticipant as below.
SimulatorTrackingParticipant simTracker = new SimulatorTrackingParticipant()
{
TrackingProfile = new TrackingProfile()
{
Name = "CustomTrackingProfile",
Queries =
{
new CustomTrackingQuery()
{
Name = all,
ActivityName = all
},
new WorkflowInstanceQuery()
{
**States = {all },**
},
new ActivityStateQuery()
{
// Subscribe for track records from all activities for all states
ActivityName = all,
States = { all },
**Arguments = {all},**
// Extract workflow variables and arguments as a part of the activity tracking record
// VariableName = "*" allows for extraction of all variables in the scope
// of the activity
Variables =
{
{ all }
}
}
}
}
};
This will now correctly capture all workflow instance states rather than just Started/Completed. You will also capture all Arguments on each activity that records tracking data rather than just the variables. This is important because the 'variable' were interested in is actually (as discussed earlier) a DelegateInArgument.
So once we have changed the tracking profile we also need to change the SimulatorTrackingParticipant.cs to extract the additional data we are now tracking.
If you change the OnTrackingRecordReceived method to include the following sections these will capture variable data and also Argument data during execution.
protected void OnTrackingRecordReceived(TrackingRecord record, TimeSpan timeout)
{
System.Diagnostics.Debug.WriteLine(
String.Format("Tracking Record Received: {0} with timeout: {1} seconds.", record, timeout.TotalSeconds)
);
if (TrackingRecordReceived != null)
{
ActivityStateRecord activityStateRecord = record as ActivityStateRecord;
if (activityStateRecord != null)
{
IDictionary<string, object> variables = activityStateRecord.Variables;
StringBuilder vars = new StringBuilder();
if (variables.Count > 0)
{
vars.AppendLine("\n\tVariables:");
foreach (KeyValuePair<string, object> variable in variables)
{
vars.AppendLine(String.Format(
"\t\tName: {0} Value: {1}", variable.Key, variable.Value));
}
}
}
if (activityStateRecord != null)
{
IDictionary<string, object> arguments = activityStateRecord.Arguments;
StringBuilder args = new StringBuilder();
if (arguments.Count > 0)
{
args.AppendLine("\n\tArgument:");
foreach (KeyValuePair<string, object> argument in arguments)
{
args.AppendLine(String.Format(
"\t\tName: {0} Value: {1}", argument.Key, argument.Value));
}
}
//bubble up the args to the UI for the user to see!
}
if((activityStateRecord != null) && (!activityStateRecord.Activity.TypeName.Contains("System.Activities.Expressions")))
{
if (ActivityIdToWorkflowElementMap.ContainsKey(activityStateRecord.Activity.Id))
{
TrackingRecordReceived(this, new TrackingEventArgs(
record,
timeout,
ActivityIdToWorkflowElementMap[activityStateRecord.Activity.Id]
)
);
}
}
else
{
TrackingRecordReceived(this, new TrackingEventArgs(record, timeout,null));
}
}
}
Hope this helps!

Can we compare two java collections with dynamic equals method?

Lets say we have a 'Client' object:
(am just mentioning the attributes and the equals method alone of the 'Client' object below!!)
public class Client {
private Long clientId;
private String clientName;
private Integer status;
//getters and setters for above attributes
.....
...
//hashCode method
....
..
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Client other = (Client) obj;
if (clientId == null) {
if (other.clientId != null)
return false;
} else if (!clientId.equals(other.clientId))
return false;
if (clientName == null) {
if (other.clientName != null)
return false;
} else if (!clientName.equals(other.clientName))
return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
return true;
}
}
From the above equals method itz clear that 'two' client objects are said to be equal if all the attributes of the two objects are identical.
Now assume a scenario where I need to compare two collections(named say incomingClients and existingClients) of Client objects.
The first collection(Collection incomingClients) was generated after reading the 'client' data from a csv/xls file.
The second collection(Collection existingClients) contains, all the existing clients currently in the system.
I can do the following code (using apache CollectionUtils)to get the 'common' clients.
Collection<Client> commonClients = (Collection<Client>)CollectionUtils.intersection(incomingClients,existingClients);
Now with the below code I can remove these commonClients from both the collections.
incomingClients.removeAll(commonClients);
existingClients.removeAll(commonClients);
The intention of removing the 'common clients objects' was that, we dont need to do 'any processing' for these records,
as we are really not at all interested in those records.
Now how can I figure out which are the entirely 'new clients' in the 'Collection incomingClients' collection?
(When I say 'new' it means a client having a new 'clientId' which doesnt exist in the 'Collection existingClients')
Also, how can I figure out which are the clients which needs 'modification'
(When I say 'modification' it means that the 'Collection incomingClients' and Collection existingClients'
have the same clientId, but, say, different 'clientName')
I know that we can do the normal 'for' loop('check below') to figure out the 'new'/'modification needed' clients.
But I thought of writing 'something new', whether we can achieve this using some classes/function in the 'Apache CollectionUtils' package.
Collection<Client> newClients = new ArrayList<Client>();
Collection<Client> toBeModifiedClients = new ArrayList<Client>();
boolean foundClient = false;
Client client = null;
for(Client incomingClient :incomingClients){
foundClient = false;
for(Client existingClient : existingClients){
if(existingClient.getClientId().equals(incomingClient.getClientId())){
client = existingClient;
foundClient = true;
break;
}
}
if(foundClient){
toBeModifiedClients.add(client);
}else{
//not found in existing. so this is completely new
newClients.add(incomingClient);
}
}
Am I 'complicating' a simple stuff??
Any thoughts??
First, yes, you are complicating "simple stuff". Your entire question could be summarized as follows:
Given collections A and B, how can I get the following using CollectionUtils:
A-B, using a particular function that determines equality
A∩B, using a particular function that determines equality
So, yes. CollectionUtils has what you need. Look at CollectionUtils.select().

Picking out Just JSON Data Returned from ASP.NET MVC3 controller Update

I've got data returned from my JavaScript client that just includes the data that has changed. That is, I may have an array with each row containing 10 columns of JSON downloaded, but on the Update, only the data that is returned to me is the data that got updated. On my update, I only want to update those columns that are changed (not all of them).
In other words, I have code like below but because I'm passing in an instance of the "President" class, I have no way of knowing what actually came in on the original JSON.
How can I just update what comes into my MVC3 update method and not all columns. That is, 8 of the columns may not come in and will be null in the "data" parameter passed in. I don't want to wipe out all my data because of that.
[HttpPost]
public JsonResult Update(President data)
{
bool success = false;
string message = "no record found";
if (data != null && data.Id > 0)
{
using (var db = new USPresidentsDb())
{
var rec = db.Presidents.FirstOrDefault(a => a.Id == data.Id);
rec.FirstName = data.FirstName;
db.SaveChanges();
success = true;
message = "Update method called successfully";
}
}
return Json(new
{
data,
success,
message
});
}
rec.FirstName = data.FirstName ?? rec.FirstName;
I would use reflection in this case because the code will be too messy like
if (data.FirstName != null)
rec.FirstName = data.FirstName
.
.
.
and so on for all the fields
Using reflection, it would be easier to do this. See this method
public static void CopyOnlyModifiedData<T>(T source, ref T destination)
{
foreach (var propertyInfo in source.GetType().GetProperties())
{
object value = propertyInfo.GetValue(source, null);
if (value!= null && !value.GetType().IsValueType)
{
destination.GetType().GetProperty(propertyInfo.Name, value.GetType()).SetValue(destination, value, null);
}
}
}
USAGE
CopyOnlyModifiedData<President>(data, ref rec);
Please mind that, this won't work for value type properties.

How to get a diff of pending changes to a model in ASP.NET MVC 2

I am working on an ASP.Net MVC app and I want to show a confirmation page after the user edits some data. What I would like to show is a list of the pending changes that the user made to the model.
For example,
Are you sure you want to make the following changes:
FieldName:
Previous Value: XXX
New Value: YYY
I know I can read my stored value from the database and compare it with the POSTed object but I want this to work generally. What would be some good ways to approach this?
To clarify, I am looking for a general way to get a "diff" of the pending changes. I already know how to get the previous and pending changes. Kind of like how TryUpdateModel() can attempt to update any Model with posted values. I'd like a magical GetPendingModelChanges() method that can return a list of something like new PendingChange { Original = "XXX", NewValue = "YYY"} objects.
You might be doing this already but I wouldn't send my model to the view, create a viewmodel. In this case I would map the model data to the viewmodel twice, my viewmodel might contain OrderInput and OrderInputOrig. Then stick OrderInputOrig in hidden fields. On post back you can compare the values and then redirect, if something changed, to a display view with the original and the changes for confirmation.
Maybe something like this:
[HttpPost]
public ActionResult Edit(CustomerInput cutomerInput)
{
var changes = PublicInstancePropertiesEqual(cutomerInput.OriginalCustomer, cutomerInput.Customer);
if (changes != null)
{
cutomerInput.WhatChangeds = changes;
return View("ConfirmChanges", cutomerInput);
}
return View();
}
public ActionResult ConfirmChanges(CustomerInput customerInput)
{
return View(customerInput);
}
from: Comparing object properties in c#
public static Dictionary<string, WhatChanged> PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
{
Dictionary<string, WhatChanged> changes = null;
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
var selfValue = type.GetProperty(pi.Name).GetValue(self, null);
var toValue = type.GetProperty(pi.Name).GetValue(to, null);
if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
if (changes == null)
changes = new Dictionary<string, WhatChanged>();
changes.Add(pi.Name, new WhatChanged
{
OldValue = selfValue,
NewValue=toValue
});
}
}
}
return changes;
}
return null;
}
Coming in very late here, but I created a library to do this on MVC models and providing "readable" diffs for humans using MVC ModelMetadata:
https://github.com/paultyng/ObjectDiff
It gives me output when I save a Model similar to:
Status: 'Live', was 'Inactive'
Phone: '123-456-7898', was '555-555-5555'
Etc.
use the TempData Dictionary.
TempData["previousValue"];
TempData["newValue"];

Resources