I'm developing custom workflow step for MS CRM 2011.
I wondering is it possible to retrieve NetworkCredentials object representing current user who running this workflow?
Does this information present in CodeActivityContext object?
Here is how Activity definition looks like:
public class CustomActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
...
}
}
Nope. I believe that you would not be able to do that. All the async jobs are done in security context of account that is used for AsyncService login.
you can find who running this workflow If you use these lines:
protected override void Execute(CodeActivityContext context)
{
IWorkflowContext _Context = context.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory _IOrganizationServiceFactory = context.GetExtension<IOrganizationServiceFactory>();
IOrganizationService xrmService = _IOrganizationServiceFactory.CreateOrganizationService(_Context.InitiatingUserId);
}
Something that could help: _Context.InitiatingUserId
and you must register your plugin in plugin registration apart from calling user
I think it depends on the type of workflow, you are using. For example I used StateMachine type, where in OnResumeBookmark method could be send object variable, where you can put any structure as interaction with user, who resumed workflow. After resuming you can set some workflow variable and use it as you wish.
Another way is use Workflow arguments of given type, when creating new Workflow instance.
What do you want to do with this credentials?
Related
I am using ASP.NET MVC 5 and Identity Framework. When I call UserManager.UpdateAsync(...) my eventhandlers on ApplicationDbContext() SaveChanges will run. Here I am using HttpContext.Current for different purposes (logging and auditing) so I must get say current user. However the whole method runs in a worker thread, and here HttpContext.Current is null.
The biggest problem that the UserManager's "sync" methods are only wrappers around the async version, so the calls are serialized, but the methods (and eventhandlers) still run in a different worker thread.
Please note this issue has nothing to do with the async/await context. In the controller after the await (or calling the 'sync' version) I have back the correct HttpContext, even the controller's method is continuing in an other thread. That's fine.
So the problem is inside the async worker which will run in both the "sync" and async versions. I think I am understanding the phenomena (but I am not happy with the fake 'sync' method versions, real sync methods would not exhibit this issue.) I just does not know how to deal/workaround it.
[btw: Would not it be more natural to implement UserManager's operarations as simple pure sync versions, then wrap them by async multithreaded wrappers?. IF we continue this async fashion without thinking we will soon invent the async assignment operator. It costs me dozens of hours (just this issue), and costs worldwide zillion dollars, I am sure in many cases less return than its price.]
Bonus: We are talking about UserManager which's impact pretty marginal, but the same principles and issues can apply any out of the box library (black box for you) which authors do not implement sync versions and or do not care about the controller thread's context. What about EF, it is not so marginal... and what about DI containers instantiation infrastructure like "request scope" or "session scope". Surely they misbehave if resolving occurs in a thread with no HttpContext.Current. Recently I refreshed SendGrid NuGet, and (as a breaking change) Deliver() method gone, and now only DeliverAsync() is existing...
I would like to have a safe reliable way, how can I access the HttpContext inside this worker for logging and audit purposes.
Sample code, the controller 'sync' version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(ApplicationUser user)
{
// validation etc
// Update() seems to be only a poor wrapper around the async version, still uses a worker thread.
var result = UserManager.Update(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
Sample code, the controller async version:
[AcceptVerbs(HttpVerbs.Post)]
public virtual async Task<ActionResult> Edit(ApplicationUser user)
{
// validation etc
var result = await UserManager.UpdateAsync(user);
// Note: HttpContext is correct here so it is not an async/await problem
// error handling, creating ActionResult etc.
}
and the event handler where HttpContext is null:
public ApplicationDbContext() : base("DefaultConnection", false)
{
InitializeAudit();
}
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
octx.SavingChanges +=
(sender, args) =>
{
// HttpContext.Current is null here
};
}
Any ideas?
As you said, this occurs because of threading. The delegate runs in a different thread, making the HttpContext inaccessible.
You can move the variable outside of the delegate, making it a closure.
private void InitializeAudit()
{
var octx = ((IObjectContextAdapter) this).ObjectContext;
HttpContext context = HttpContext.Current;
octx.SavingChanges +=
(sender, args) =>
{
// context is not null
};
}
You are using asp.net identity through owin,
so one instance of the dbcontext is created per request,
and you can get this reference from anywhere in the request pipeline.
nb. this is handy but i think the dbcontext shouldn't be accessed outside the manager.
In asp.net identity design, only the manager should be aware of the store.
I believe the dbcontext is exposed because several asp.net identity middleware have a dependance on it.
But, it could help resolve you problem:
Allow your custom dbcontext handler to be set outside the class:
public EventHandler SavingChangesEventHandler
{
set
{
(((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext).SavingChanges += value;
}
}
Declare a custom ActionFilter class and register it, then override OnActionExecuting:
Filtering in ASP.NET MVC
https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx
public class CustomizeAppDbcontextFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var dbcontext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
var currentuser = HttpContext.Current.User;
dbcontext.SavingChangesEventHandler = (sender, args) =>
{
// use currentuser
};
}
}
you may need these using statements to be able to call the identity.owin extension methods:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
You should be in the controller thread because OnActionExecuting is wrapping the controller action.
I did not test it, so it may need some polishing but the concept should work.
I'm new to Windows Workflow and am using 4.5 to create a long-running workflow. I did a lot of online search, trying to find a way to create a Bookmark and ResumeBookmark without user input. The info I've read so far all requires a Console.ReadLine (user input) in order to resume a Bookmark. Is Bookmark only used for human input? I'm using the Delay Activity for now, but would like to use Bookmark.
My Workflow.xaml is like this:
Send email to reviewers, who are asked to complete their respective task. The
email is just a notification. There is no approve or reject button.
Delay Activity. This is to make the workflow persist in the persistence
database.
Check another database to see if some data are updated by reviewers.
Delay Activity again, if reviewers have not updated the data.
Send email to approver. if the data are updated. Approver's response will be recorded in the database. The email is just a notification.
Delay Activity again, waiting for approver's updating response in database.
and so on.
I'd really appreciate your help.
Bookmarks do not require user input.
You create a bookmark inside an activity:
context.CreateBookmark("bookmarkName", new BookmarkCallback(OnResumeBookmark));
Where "OnResumeBookmark" is a method in your activity.
Then when you resume the workflow you use this:
WorkflowApplication wfApp= new WorkflowApplication(new NameOFWorkflow());
wfApp.Run();
wfApp.ResumeBookmark("bookmarkName");
OnResumeBookmark will then execute.
Here is a fuller version http://msdn.microsoft.com/en-us/library/ee191721(v=vs.110).aspx
The stuff in there about console.read is just a way to show you how the bookmark name can be a variable rather than a string:
context.CreateBookmark(BookmarkName.Get(context), <-- get name from the InArgumen
Here a sample code of a custom activity:
public sealed class WaitForResponse<TResult> : NativeActivity<TResult>
{
public string ResponseName { get; set; }
protected override bool CanInduceIdle
{
get
{
return true;
}
}
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(this.ResponseName, new BookmarkCallback(this.ReceivedResponse));
// Put code here...
}
void ReceivedResponse(NativeActivityContext context, Bookmark bookmark, object obj)
{
this.Result.Set(context, (TResult)obj);
}
This activity will run the method Execute and wait (persist/unload) until a ResumeBookmark. The ResumeBookmark can be a WCF call or a invoke of WorkflowApplication.ResumeBookmark.
I'm writing a customized activity for TFS build process workflow, e.g. guideline here.
In my C# CodeActivity .Execute() method, I want to run another activity, e.g. ConvertWorkspaceItem as descibed here.
How can I do that?
Try doing some research with a NativeActivity instead of a CodeActivity, so the execution context allows you to schedule other child activities.
E.g.:
class YourActivity : NativeActivity
{
protected override void Execute(NativeActivityContext context)
{
ConvertWorkspaceItem it = new ConvertWorkspaceItem();
context.ScheduleActivity(it);
}
}
I'm a newbie about the usage of Asp.NET membership capabilities and I want to know if it could be a good practice to deny the access of a whole page using code like this:
public partial class AdminPage : Page
{
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
protected void Page_Load(object sender, EventArgs e)
{
...
}
}
I suspect that it is not a good way to do things, but I would like to know why !
Thanks.
Small point-- put the attribute on the class. This will cause the page to raise a Security Exception as soon as you navigate to it without appropriate rights. To keep users from viewing this page, check their credentials before displaying the URL. The attribute on the class is the strong guarantee that no ordinary user will run so much as a line of the code in that class.
Yes, this is a good technique for these reasons:
The attribute works when the thread principle and the HttpContext User object are set, with a suitable IPrincipal and IIdentity. (All this would happen in the Request Authentication event in global asax) These interfaces are defined by Microsoft, well documented and available in any context, any application that runs on a MS Operating system. So any half competent developer you grab off the street could be familiar with this before they start to read your code.
Also, since Thread's IPrincipal and IIdentity are used by Microsoft (it could have been any large company with a large user base), it's battle tested code. You can still do something stupid, but the existing patterns are there to help you fall into the pit of success.
On the other hand, if you are putting a custom object into Session, a magic cookie or some other token, then the maintenance developer will have to learn how it works from scratch and then review it to see if has exploitable vulnerabilities.
I think you will need a base class for all your pages, e.g.:
public abstract class BasePage : Page
{
// Note:
// 1. check on init, not on load
// 2. override protected method, not handle event
protected override OnInit(EventArgs e)
{
// check permissions here
}
}
I'm using Castle Core to create a custom attribute and interceptor to inject security checks into our code using attributes.
e.g. [Security("Role1")]
In the implementation of the interceptor:
public class SecurityInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
object o;
MethodInfo mi = invocation.Method;
SecurityAttribute[] atts = (SecurityAttribute[])mi.GetCustomAttributes(typeof(SecurityAttribute), true);
// if method not marked with Security attribute, then pass on call
if (atts.Length == 0)
{
invocation.Proceed();
}
else
{
//for now assume that there is only one security attribute on the method
//do some security test
{
invocation.Proceed();
}
}
}
In the "do some security test" section above, I need access to the HttpContext.Session object in order to retrieve some saved objects to do the security test.
Assume the method that this attribute is on, is a code-behind asp.net page, i.e an instance of the Page class)
I can't just use this.Context in the attribute like this [Security("Role1", this.Context)]
as attributes don't allow that.
So how do I get access to the httpContext inside the Intercept method?
Or is there a better way of doing this in an aspect-like way?
The InvocationTarget property of the IInvocation instance has the target object. So in your case, if you are certain that the interception happens on a Page object, you should be able to do this:
var page = (Page)invocation.InvocationTarget;
If that is not always the case, you should gain access to the HTTP context in another way.
It's true, as Ben points out, that HttpContext.Current gives you access to the current HttpContext from anywhere, but accessing that static property is just icky. There's a better way, however, and that is by registering a factory method that allows for injection of the session state:
container.Register(
Component.For<ISessionState>()
.UsingFactoryMethod(k => new SessionWrapper(HttpContext.Current.Session)
.Lifestyle.PerWebRequest));
assuming that you have created the ISessionState interface and an appropriate wrapper that has the API you wish to use when interacting with the ASP.NET HttpSessionState object.
Now, since the interceptor is pulled from the container like everything else, it can depend on ISessionState:
public class SecurityInterceptor : IInterceptor
{
public SecurityInterceptor(ISessionState sessionState)
{
//...
}
}
which makes your interceptor nice and testable.
There's probably many other ways to do this, and possible better ways as well. This is just an idea on how you can get on with your project :)
You can use HttpContext.Current.Session from anywhere so long as the code is being called from an ASP.net process.