Skip to content
This repository has been archived by the owner on Sep 18, 2021. It is now read-only.

Expose DefaultViewServiceOption list props as IEnumerables. #3050

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

johnkors
Copy link
Contributor

@johnkors johnkors commented Jul 11, 2016

Fixes #2947. NB! Breaking change & needs a bump of major.

The DefaultViewServiceOption / DefaultViewServiceRegistration allows mutating state on a registred singleton, causing possible bad side effects. This PR changes the option API to use IEnumerable instead of IList - restricting access to modify the list directly.

Example of a bad side effect caused by modifying one of the lists in a class inheriting from the DefaultViewService: #2089 (comment)

@johnkors johnkors changed the title Expose DefaultViewServiceOption list props as IEnumerables. Fixes #2947 Expose DefaultViewServiceOption list props as IEnumerables. Jul 11, 2016
@dnfclas
Copy link

dnfclas commented Aug 21, 2016

@johnkors, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR.

Thanks, DNFBOT;

@brockallen brockallen added this to the 3.0 milestone Aug 22, 2016
@brockallen brockallen self-assigned this Aug 22, 2016
@brockallen
Copy link
Member

Ok, will visit once we have a v3 branch. Thx

@MrJerB
Copy link

MrJerB commented Apr 11, 2017

Hi all,
I am currently experiencing an issue where I modify the config on each request (dynamically adding stylesheets depending on an acr_values) in the constructor of my class (which inherits from DefaultViewService). This feels really messy and I'm not even sure if this is thread safe as my assumption was that the "config" passed to the constructor is factory generated. If anyone can suggest a better method than the following, please:

public class BrandedViewService : DefaultViewService
{
	private static readonly string BrandAcr = "brand";

	public BrandedViewService(DefaultViewServiceOptions config, IViewLoader viewLoader) : base(config, viewLoader)
	{
		// Disable views to re-render styles correctly
		config.CacheViews = false;

		// Remove any branding stylesheets from previous config
		config.Stylesheets = new DefaultViewServiceOptions().Stylesheets;

		// Logic for branding
		var req = HttpContext.Current.Request;
		var env = req.GetOwinContext().Environment;
		var signInId = req["signin"];

		if (signInId == null)
			return;

		var signInMessage = env.GetSignInMessage(signInId);
		var brand = signInMessage?.AcrValues?.FirstOrDefault(acr => acr.StartsWith(BrandAcr + ":", System.StringComparison.Ordinal))?.Substring(BrandAcr.Length + 1);

		if (brand != null)
		{
			config.Stylesheets.Add("~/IdentityServer/Brands/test.css");
		}
	}
}

I am registering the dependency as follows:

factory.ViewService = new DefaultViewServiceRegistration<IdentityServer.BrandedViewService>()
{
	Mode = RegistrationMode.InstancePerHttpRequest
};

I am commenting here because this pull request might solve the issue or at least allow me to do this in a cleaner fashion.

@johnkors
Copy link
Contributor Author

@MrJerB , the work-around for now is to not mutate the singleton-scoped config's lists.

For example:

public class CustomViewService : DefaultViewService 
{
        public CustomViewService(DefaultViewServiceOptions options, IViewLoader viewLoader, OwinEnvironmentService owinservice) : base(options, viewLoader)
        {
            _owinservice = owinservice;
        }

        protected override Task<Stream> Render(CommonViewModel model, string page)
        {
            var acrs = _owinservice.Environment.GetSignInMessage().AcrValues;
            var styleSheets = config.Stylesheets.ToList();
            var isBrandReq = acrs.Any(s => s.Equals("BRAND"));
            if(isBrandReq)
                styleSheets.Add("~/IdentityServer/Brands/test.css");

            return base.Render(model, page, styleSheets, config.Scripts);
        }
}

@MrJerB
Copy link

MrJerB commented Apr 11, 2017

Thanks @johnkors and another plus one for showing me how to obtain the OWIN context through dependency injection 👍

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants