简体   繁体   中英

MVC Get/Impersonate Windows User In Repository

I have an intranet application that uses the Windows username and passes that to a procedure to return data.

  1. I'm using dependency injection, but I don't believe I have the method to get the username separated properly.
  2. I'm trying to keep this secure by not passing in the username as a parameter, but I also want to be able to impersonate (or bypass my GetWindowsUser() method) and send in another username so I can test results for other users.
    • One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository.

WEB API CONTROLLER

public class DropDownDataController : ApiController
{
    private IDropDownDataRepository _dropDownDataRepository;        

    //Dependency Injection using Unity.WebAPI NuGet Package
    public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
    {
        _dropDownDataRepository = dropDownDataRepository;
    }

    [HttpGet]
    public HttpResponseMessage MyList()
    {
        try
        {
            return _dropDownDataRepository.MyList();
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }
}

REPOSITORY

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    private DatabaseEntities db = new DatabaseEntities();

    public HttpResponseMessage MyList()
    {
        //(This should be separated somehow, right?) 
        //Create a new instance of the Utility class
        Utility utility = new Utility();
        //Grab the windowsUser from the method
        var windowsUser = utility.GetWindowsUser();

        //Pass windowsUser parameter to the procedure
        var sourceQuery = (from p in db.myProcedure(windowsUser)
                           select p).ToList();

        string result = JsonConvert.SerializeObject(sourceQuery);
        var response = new HttpResponseMessage();
        response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");

        return response;            
    }
}

INTERFACE

public interface IDropDownDataRepository : IDisposable
{
    HttpResponseMessage MyList();        
}

UTILITY CLASS

public class Utility
{
    public string GetWindowsUser()
    {
        //Get the current windows user
        string windowsUser = HttpContext.Current.User.Identity.Name;        

        return windowsUser;
    }
}

UPDATE 1

In addition to what Nikolai and Brendt posted below, the following is also needed to allow Web Api controllers work with the session state. Accessing Session Using ASP.NET Web API

Abstract the Utility class and inject it into the repository. Then you can stub or mock for testing.

public interface IUtility
{
    string GetWindowsUser();
}

public class TestUtility : IUtility
{
    public string GetWindowsUser()
    {
        return "TestUser";
    }
}

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{

    private IUtility _utility;

    public DropDownDataRepository(IUtility utility)
    {
        _utility = utility;
    }

}

EDIT

Also the repository should not return an HTTPResponseMessage type it should just return a List<T> of the domain model you're accessing.

ie

public List<Model> MyList()
{
    //Grab the windowsUser from the method
    var windowsUser = _utility.GetWindowsUser();

    //Pass windowsUser parameter to the procedure
    var sourceQuery = (from p in db.myProcedure(windowsUser)
                       select p).ToList();

    return sourceQuery           
}

Then move the JSON portion to the controller.

One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository.

Potentially, if you add in a dependency to session, you need to isolate it, eg

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    // ... other fields

    private ISession session;

    public DropDownDataRepository(ISession session)
    {
        this.session = session;
    }

    public HttpResponseMessage MyList()
    {
         var myUserName = this.session.UserName;
         // ... etc

With ISession being something like:

 public interface ISession
 {
      string UserName { get; }
 }

Implemented as:

 public class MySession : ISession
 {
     public string UserName
     {
         get
         {
            // potentially do some validation and return a sensible default if not present in session
            return HttpContext.Current.Session["UserName"].ToString();
         }
     }

 }

Of course there is the potential to decouple this MySession class from HttpContext if desired.


With regards to this:

    //(This should be separated somehow, right?) 
    //Create a new instance of the Utility class
    Utility utility = new Utility();

Yes, anytime you create a new object you are tightly coupling them together, which will give you issues, for example, if you try to unit test it in isolation.

In this instance you could extract an IUtility interface from Utility :

public class Utility : IUtility
{
    string GetWindowsUser();
}

Then:

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    // ... other fields

    private IUtility utility;

    public DropDownDataRepository(IUtility utility)
    {
        this.utility = utility;
        // .... etc

Then you have removed the depenedency between Utility and DropDownDataRepository , and can substitute in another type or mock with ease.

I got a lot of help from Nikolai and Brent and got most of the way there with their posted answers, but ended up figuring out the complete answer on my own. The problems I was having were related to not being able to access session variables in a WebAPI. So, I'm sure there are cleaner solutions to this, but I definitely improved what I had and came up with the following code, which works.

This answer was needed to allow access to the session variable in Web Api - Accessing Session Using ASP.NET Web API

GLOBAL.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
        UnityConfig.RegisterComponents();
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    //Added to allow use of session state in Web API
    protected void Application_PostAuthorizeRequest()
    {
        if (IsWebApiRequest())
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

    //Added to allow use of session state in Web API
    private bool IsWebApiRequest()
    {
        return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
    }

    protected void Session_Start(Object sender, EventArgs e)
    {
        //Default set the session variable to none
        Session["_impersonatedUser"] = "none";
    }

    protected void Session_End(Object sender, EventArgs e)
    {
        //Reset the session variable to blank
        Session["_impersonatedUser"] = "";
    }
}

UNITY.config

public static class UnityConfig
{
    public static void RegisterComponents()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // e.g. container.RegisterType<ITestService, TestService>();


        container.RegisterType<IDropDownDataRepository, DropDownDataRepository>();
        container.RegisterType<IUtilityRepository, UtilityRepository>();
        container.RegisterType<ISessionRepository, SessionRepository>();

        //MVC5
        //Unity.MVC5 NuGet Package
        DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));

        //WEB API 
        //Unity.WebApi NuGet Package
        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
    }
}

WEB API CONTROLLER

public class DropDownDataController : ApiController
{
    private IDropDownDataRepository _dropDownDataRepository;      

    //Dependency Injection using Unity.WebAPI NuGet Package
    public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
    {
        _dropDownDataRepository = dropDownDataRepository;
    }

    [HttpGet]
    public HttpResponseMessage MyList()
    {
        try
        {
            var sourceQuery = _dropDownDataRepository.MyList();

            //JSON stuff moved to controller
            string result = JsonConvert.SerializeObject(sourceQuery);
            var response = new HttpResponseMessage();
            response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");

            return response;
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }

    protected override void Dispose(bool disposing)
    {
        _dropDownDataRepository.Dispose();
        base.Dispose(disposing);
    }
}

DROPDOWNDATA REPOSITORY

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    private DatabaseEntities db = new DatabaseEntities();
    private IUtilityRepository _utilityRepository;
    private ISessionRepository _sessionRepository;

    //Dependency Injection of Utility and Session
    public DropDownDataRepository(IUtilityRepository utilityRepository, ISessionRepository sessionRepository)
    {
        _utilityRepository = utilityRepository;
        _sessionRepository = sessionRepository;
    }

    //Changed to a list here
    public List<MyProcedure> MyList()
    {
        string windowsUser;
        //Check the session variable to see if a user is being impersonated
        string impersonatedUser = _sessionRepository.ImpersonatedUser;

        //Grab the windowsUser from the Utility Repository
        windowsUser = _utilityRepository.GetWindowsUser();

        if (impersonatedUser != "none")
        {
            windowsUser = impersonatedUser;
        }        

        //Pass windowsUser parameter to the procedure
        var sourceQuery = (from p in db.MyProcedure(windowsUser)
                           select p).ToList();

        return sourceQuery;            
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

DROPDOWNDATA INTERFACE

public interface IDropDownDataRepository : IDisposable
{
    //Changed to list here
    List<MyProcedure> MyList();
}

UTILITY REPOSITORY

public class UtilityRepository : IUtilityRepository
{
    public string GetWindowsUser()
    {
        //Get the current windows user
        string windowsUser = HttpContext.Current.User.Identity.Name;

        return windowsUser;
    }
}

UTILITY INTERFACE

public interface IUtilityRepository
{
    string GetWindowsUser();
}

SESSION REPOSITORY

 public class SessionRepository : ISessionRepository
{
    public string ImpersonatedUser
    {
        get
        {
            return HttpContext.Current.Session["_impersonatedUser"].ToString();
        }
    }
}

SESSION INTERFACE

public interface ISessionRepository
{
    string ImpersonatedUser { get; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM