How do I centralize code shared by multiple Web API controllers? - asp.net

I have close to 10 controllers that currently share the same code. The code is pretty simple, it just checks if a set of data is null and checks if the current user has permission to access the data.
If there is an issue, I throw an HttpResponseException.
The code works when it is sitting in each controller. I have also managed to centralize the code but I think the way I have done it is wrong. I've created a new class which inherits ApiController and then I have the controllers inheriting my new class. This is the only way I could get the HttpResponseExceptions working. Code is as follows:
//New centralized class:
public class AuthorizationClass : ApiController
{
private DataModel db = new DataModel();
public async Task checkUserisValid(int user_id)
{
user_list user_list = await db.user_list.FindAsync(user_id);
if (user_list == null)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest,"This user does not exist"));
}
int businessID = user_list.business_id;
var result = checkAccess(User.Identity.Name, businessID);
if (result.Count <= 0)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have access to modify this business"));
}
}
public static List<user_details> checkAccess(string userName, int id)
{
//code which checks if the user is in the right tables
return checkAccess.ToList();
}
}
Then in the controller class, I have:
public class MyController : AuthorizationClass
{
public async Task<IHttpActionResult> Postnew_table(int id, new_table new_table)
{
await checkUserisValid(id);
//rest of controller
}
}
I tried to do it in different ways but this is the only way I could get it working with HttpResponseException. Is there a better way to do this without inheriting classes or is this the only way to do what I am after?
Thanks.

You could just move these 2 methods to some static helper class in a common assembly, you mention that Request is an instance variable on the controller, just pass it to the method.
public static class SomeHelper
{
public static async Task checkUserisValid(int user_id, DataModel db, Request request, User user)
{
user_list user_list = await db.user_list.FindAsync(user_id);
if (user_list == null)
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest,"This user does not exist"));
}
int businessID = user_list.business_id;
var result = checkAccess(user.Identity.Name, businessID);
if (result.Count <= 0)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have access to modify this business"));
}
}
public static List<user_details> checkAccess(string userName, int id)
{
//code which checks if the user is in the right tables
return checkAccess.ToList();
}
}

Related

WF 4 OnUnhandledException not hit

I've created a custom activity which contains as a Body another Activity.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
}
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
throw new Exception(propagatedException.Message);
}
When an exception is thrown during the execution of the Body, ma handler for the OnFaulted is hit.
My execution starts with a call to static method Run of the WorkflowApplication class. My WorkflowApplication instance has a handler associated for the OnUnhandledException event.
instance.OnUnhandledException +=
delegate(WorkflowApplicationUnhandledExceptionEventArgs args)
{
Console.WriteLine(args.ExceptionSource);
waitEvent.Set();
return UnhandledExceptionAction.Cancel;
};
But regardless of what happens when the Activity hosted in the Body is executed, i never reach the handler defined above. I thought that if i throw an exception from the OnFaulted, i will be able to redirect the flow to the OnUnhandledException but i was wrong. Any ideas ?
I need this in order to centralize my errors, check them and display messages accordingly. Also i need a way to stop the execution and so on and i don't want to define handlers all over the application. Is there any way to accomplish this ?
As Will suggested, i will post what i did to handle my scenario.
Basically, in my custom activity i have hosted an Assign :
[Browsable(false)]
public Activity Body { get; set; }
Activity System.Activities.Presentation.IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new Assignment()
{
Body = new Assign() { DisplayName = "" }
};
}
I've added this code to my Execute method :
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
I was trying to run this Assignment by giving an array a negative value as index and and an exception was thrown. This, somehow ended my execution but no handler for the events of my WorkflowApplication instance were hit.
Here is the method given as a callback when executing the body ( in our case the Assign activity ) :
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
faultContext.HandleFault();
CommunicationExtension ce = faultContext.GetExtension<CommunicationExtension>();
ITextExpression toTextExpression = (propagatedFrom.Activity as Assign).To.Expression as ITextExpression;
string valueTextExpression = string.Empty;
if ((propagatedFrom.Activity as Assign).Value != null)
{
if ((propagatedFrom.Activity as Assign).Value.Expression != null)
valueTextExpression = (propagatedFrom.Activity as Assign).Value.Expression.ToString();
}
if (ce != null)
{
ce.AddData(string.Format("{0} found on Assignment definition [{1} = {2}]", propagatedException.Message, toTextExpression.ExpressionText, valueTextExpression));
}
}
The trick was to call :
faultContext.HandleFault();
and use CommunicationExtension to allow me to to display the erros in the GUI.
The code for this class is trivial :
public class CommunicationExtension
{
public List<string> Messages { get; set; }
public CommunicationExtension()
{
Messages = new List<string>();
}
public void AddData(string message)
{
if (string.IsNullOrEmpty(message))
return;
Messages.Add(message);
}
}
Use this to add the extension:
CommunicationExtension ce = new CommunicationExtension();
instance.Extensions.Add(ce);
where instance is my WorkflowApplication instance.
I understood that for each instance of the workflow application we have one instance of its extension class. So i can send messages like this from all my custom activities in order to display their status.
I hope this scenario can help other people too.

accessing virtual actionresult in mvc

I want to know how can I access virtual actionresult, since I haven't seen so far this type of actionresults. I've read little bit and I can see that is related to t4mvc but I'm not sure how can I access it.
This is what I have, ScheduleController, which has this actionresult for example:
[Authorize]
public virtual ActionResult AdminSport(int sportId)
{
Sport sport = _rep.GetSport(sportId);
if(sport == null)
{
return this.ViewNotFound();
}
if(!User.IsScheduleAdmin(sport))
{
return this.ViewNotAuthorized();
}
var ret = _rep.ListScheduleEntriesForAD(sport);
return View(ret);
}
Then, the only other instance of that controller I found was in T4MVC, and this is whats in that file:
[NonAction]
partial void AdminSportOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, int sportId);
[NonAction]
public override System.Web.Mvc.ActionResult AdminSport(int sportId)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.AdminSport);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "sportId", sportId);
AdminSportOverride(callInfo, sportId);
return callInfo;
}
My question is how can I access that specific action, for example if I type in the browser: /Schedule/AdminSport I'm getting an error.
Any idea what/how do I need to fix this?

Javafx pass login token from LoginController to CalendarController

I am using Angela's framework for screens management and I am trying to write a very simple Calendar-application. While writing the UI and the controllers and using the aforementioned framework, all screens initialize immediately on program start. This means I have no idea when the user is actually looking at a certain view.
I need the login-token from the server (assigned in the LoginController) to fire a changed value event of some kind in the CalendarController that is currently running in the background (I presume). At the moment I don't know when the Calendar.fxml is visible and/if the user is logged in, and hence I don't know how to structure my logic to make a function start in CalendarController ONLY after the login-token has been set.
Been stuck a few days here, any help would be greatly appreciated. I have tried using an ObservableList and Listlistener-interface to no avail. Here is the respective part of my LoginController. TokenFactory is a class of static fields and methods (mostly trying to debug).
#FXML
public boolean login() throws JSONException, UnirestException {
if(validateUsernameField() && validatePasswordField()) {
HttpResponse<JsonNode> jsonResponse = Unirest.post(TokenFactory.getSERVER_ADR())
.field("username", usernameField.getText())
.field("password", passwordField.getText())
.asJson();
if ( ((String) jsonResponse.getBody().getObject().get("message")).equalsIgnoreCase("OK")) {
String token = ((String) jsonResponse.getBody().getObject().get("token"));
//Ignore JSON to debug
TokenFactory.setToken("fakeToken123");
responseLabel.setText("Logging in...");
myController.setScreen(ScreensFramework.CalendarID);
return true;
} else {
responseLabel.setText("Wrong username or password.");
passwordField.clear();
}
} return false;
}
//Screen management
ScreensController myController;
You have a bunch of options here:
First option: instead of loading all the screens at startup, just load the calendar screen when the login is successful. Then your CalendarController's initialize() method can basically assume the user is logged in.
Second option: modify the framework so that it either returns a reference to the controllers when it loads them, or gives you access to the controllers once loaded. The first version of this would look like:
public <T extends ControlledScreen> T loadScreen(String name, String resource) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Parent loadScreen = (Parent) myLoader.load();
T myScreenControler = myLoader.getController();
myScreenControler.setScreenParent(this);
addScreen(name, loadScreen);
return myScreenControler ;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
Now when you first load the calendar screen, you can get a reference to its controller:
CalendarController calendarController = screensController.loadScreen(...);
so now when you are successfully logged in, you can invoke a method on the calendarController. Note the return type of loadScreen(...) has changed, so you may need to modify other code accordingly.
Alternatively, you could introduce a new map in ScreensController:
public class ScreensController extends StackPane {
private Map<String, Node> screens = new HashMap<>();
private Map<String, ControlledScreen> controllers = new HashMap<>();
// ...
public boolean loadScreen(String name, String resource) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Parent loadScreen = (Parent) myLoader.load();
ControlledScreen myScreenControler = ((ControlledScreen) myLoader.getController());
myScreenControler.setScreenParent(this);
addScreen(name, loadScreen);
// also save the controller:
controllers.put(name, myScreenControler);
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
// ...
// new method to retrieve controller:
public ControlledScreen getController(String name) {
return controllers.get(name);
}
// modify the remove method to clean up the controller as well:
public boolean unloadScreen(String name) {
if (screens.remove(name) == null) {
System.out.println("Screen didn't exist");
return false;
} else {
controllers.remove(name);
return true;
}
}
}
Now when the user logs in, you can do
CalendarController calendarController =
(CalendarController) myController.getController(ScreensFramework.CalendarID);
and invoke whatever method you need on calendarController.
Third option: create a BooleanProperty loggedIn = new SimpleBooleanProperty(); and just set it to true when the user is logged in. Then arrange for your CalendarController to be able to observe it and react when it changes. I like this option less, because arranging for the CalendarController to see the loggedIn property will almost certainly involve some kind of additional coupling between that controller and another class, but it is possible.

Missing owin context

I'm trying to use the ResourceAuthorize attribute from Thinktecture.IdentityModel, but everything stops because there is no owin context.
I have a owin startup class which setups the authorization manager
[assembly: OwinStartup(typeof(My.WebApi.Startup))]
namespace My.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
AuthConfig.Configure(app);
}
}
}
public class AuthConfig
{
public static void Configure(IAppBuilder app)
{
app.UseResourceAuthorization(new ResourceAuthorizationMiddlewareOptions
{
Manager = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IResourceAuthorizationManager)) as IResourceAuthorizationManager
});
}
}
and I know that it is detected and invoked. But later on, when hitting the following code from IdentityModel, I get a null pointer exception:
public static Task<bool> CheckAccessAsync(this HttpRequestMessage request, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
request.GetOwinContext().Authentication.User ?? Principal.Anonymous,
actions,
resources);
return request.CheckAccessAsync(authorizationContext);
}
I have stepped through and sees that it's caused by the GetOwinContext() returning null, since there is no MS_OwinContext or MS_OwinEnvironment property on the request.
What am I missing?
UPDATE:
I have found that i have an owin.environment property available, but it's part of the `HttpContextWrapper, not the request.
By searching around, I found some code inside of System.Web.Http.WebHost.HttpControllerHandler that looks like it should have converted the owin.environment to an MS_OwinEnvironment, but apparently, that code is never called in my case...
internal static readonly string OwinEnvironmentHttpContextKey = "owin.Environment";
internal static readonly string OwinEnvironmentKey = "MS_OwinEnvironment";
internal static HttpRequestMessage ConvertRequest(HttpContextBase httpContextBase, IHostBufferPolicySelector policySelector)
{
HttpRequestBase requestBase = httpContextBase.Request;
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod), requestBase.Url);
bool bufferInput = policySelector == null || policySelector.UseBufferedInputStream((object) httpContextBase);
httpRequestMessage.Content = HttpControllerHandler.GetStreamContent(requestBase, bufferInput);
foreach (string str in (NameObjectCollectionBase) requestBase.Headers)
{
string[] values = requestBase.Headers.GetValues(str);
HttpControllerHandler.AddHeaderToHttpRequestMessage(httpRequestMessage, str, values);
}
HttpRequestMessageExtensions.SetHttpContext(httpRequestMessage, httpContextBase);
HttpRequestContext httpRequestContext = (HttpRequestContext) new WebHostHttpRequestContext(httpContextBase, requestBase, httpRequestMessage);
System.Net.Http.HttpRequestMessageExtensions.SetRequestContext(httpRequestMessage, httpRequestContext);
IDictionary items = httpContextBase.Items;
if (items != null && items.Contains((object) HttpControllerHandler.OwinEnvironmentHttpContextKey))
httpRequestMessage.Properties.Add(HttpControllerHandler.OwinEnvironmentKey, items[(object) HttpControllerHandler.OwinEnvironmentHttpContextKey]);
httpRequestMessage.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, (object) HttpControllerHandler._retrieveClientCertificate);
httpRequestMessage.Properties.Add(HttpPropertyKeys.IsLocalKey, (object) new Lazy<bool>((Func<bool>) (() => requestBase.IsLocal)));
httpRequestMessage.Properties.Add(HttpPropertyKeys.IncludeErrorDetailKey, (object) new Lazy<bool>((Func<bool>) (() => !httpContextBase.IsCustomErrorEnabled)));
return httpRequestMessage;
}
UPDATE 2:
Inside of mvc controllers, the context is available. But not in webapi controllers.
A team mate found a solution. He simply added the following line to the owin startup class:
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Why this solves the issue is another mystery, though. But we are using wsFederation, so I guess it's needed some how. But what if we didn't use wsFed? Would we still need it to get a context? Who knows...

Using Forms Authentication with Web API

I've got a Web Forms application which I'm trying to use the new Web API beta with. The endpoints I'm exposing should only be available to an authenticated user of the site since they're for AJAX use. In my web.config I have it set to deny all users unless they're authenticated. This works as it should with Web Forms but does not work as expected with MVC or the Web API.
I've created both an MVC Controller and Web API Controller to test with. What I'm seeing is that I can't access the MVC or Web API endpoints untill I authenticate but then I can continue hitting those endpoints, even after closing my browser and recyling the app pool. But if I hit one of my aspx pages, which sends me back to my login page, then I can't hit the MVC or Web API endpoints untill I authenticate again.
Is there a reason why MVC and Web API are not functioning as my ASPX pages are once my session is invalidated? By the looks of it only the ASPX request is clearing my Forms Authentication cookie, which I'm assuming is the issue here.
If your web API is just used within an existing MVC application, my advice is to create a custom AuthorizeAttribute filter for both your MVC and WebApi controllers; I create what I call an "AuthorizeSafe" filter, which blacklists everything by default so that if you forget to apply an authorization attribute to the controller or method, you are denied access (I think the default whitelist approach is insecure).
Two attribute classes are provided for you to extend; System.Web.Mvc.AuthorizeAttribute and System.Web.Http.AuthorizeAttribute; the former is used with MVC forms authentication and the latter also hooks into forms authentication (this is very nice because it means you don't have to go building a whole separate authentication architecture for your API authentication and authorization). Here's what I came up with - it denies access to all MVC controllers/actions and WebApi controllers/actions by default unless an AllowAnonymous or AuthorizeSafe attribute is applied. First, an extension method to help with custom attributes:
public static class CustomAttributeProviderExtensions {
public static List<T> GetCustomAttributes<T>(this ICustomAttributeProvider provider, bool inherit) where T : Attribute {
List<T> attrs = new List<T>();
foreach (object attr in provider.GetCustomAttributes(typeof(T), false)) {
if (attr is T) {
attrs.Add(attr as T);
}
}
return attrs;
}
}
The authorization helper class that both the AuthorizeAttribute extensions use:
public static class AuthorizeSafeHelper {
public static AuthActionToTake DoSafeAuthorization(bool anyAllowAnonymousOnAction, bool anyAllowAnonymousOnController, List<AuthorizeSafeAttribute> authorizeSafeOnAction, List<AuthorizeSafeAttribute> authorizeSafeOnController, out string rolesString) {
rolesString = null;
// If AllowAnonymousAttribute applied to action or controller, skip authorization
if (anyAllowAnonymousOnAction || anyAllowAnonymousOnController) {
return AuthActionToTake.SkipAuthorization;
}
bool foundRoles = false;
if (authorizeSafeOnAction.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnAction.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
else if (authorizeSafeOnController.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnController.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
if (foundRoles && !string.IsNullOrWhiteSpace(rolesString)) {
// Found valid roles string; use it as our own Roles property and auth normally
return AuthActionToTake.NormalAuthorization;
}
else {
// Didn't find valid roles string; DENY all access by default
return AuthActionToTake.Unauthorized;
}
}
}
public enum AuthActionToTake {
SkipAuthorization,
NormalAuthorization,
Unauthorized,
}
The two extension classes themselves:
public sealed class AuthorizeSafeFilter : System.Web.Mvc.AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(filterContext);
return;
case AuthActionToTake.Unauthorized:
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
public sealed class AuthorizeSafeApiFilter : System.Web.Http.AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext actionContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(actionContext);
return;
case AuthActionToTake.Unauthorized:
HttpRequestMessage request = actionContext.Request;
actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized);
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
And finally, the attribute that can be applied to methods/controllers to allow users in certain roles to access them:
public class AuthorizeSafeAttribute : Attribute {
public string Roles { get; set; }
}
Then we register our "AuthorizeSafe" filters globally from Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeFilter());
}
public static void RegisterWebApiFilters(HttpFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeApiFilter());
}
Then to open up an action to eg. anonymous access or only Admin access:
public class AccountController : System.Web.Mvc.Controller {
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
// ...
}
}
public class TestApiController : System.Web.Http.ApiController {
// GET API/TestApi
[AuthorizeSafe(Roles="Admin")]
public IEnumerable<TestModel> Get() {
return new TestModel[] {
new TestModel { TestId = 123, TestValue = "Model for ID 123" },
new TestModel { TestId = 234, TestValue = "Model for ID 234" },
new TestModel { TestId = 345, TestValue = "Model for ID 345" }
};
}
}
It should work in Normal MVC controller. you just need to decorate the action with [Authorize] attribute.
In web api you need to have custom authorization. you may find below link helpful.
http://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han
If you are using the MVC Authorize attribute it should work the same way on for the WebAPI as for normal MVC controllers.

Resources