Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2024/how-security-disabler-works

How SecurityDisabler and similar 'switcher' objects work in Sitecore

You've probably used these objects, but did you ever think about how they work?

Published 08 April 2024

Just because stuff is "old" doesn't mean it's not interesting... I found myself having a discussion with a colleague recently about the state management patterns that Sitecore uses for things like SecurityDisabler and how they work in the ASP.Net pipeline. It's not new tech, but it is an interesting pattern which you might find uses for outside your XP implementations...

So what's the problem being addressed?

When you're writing the kind of framework code that Sitecore provide for websites, you're likely to come across situations where there's some sort of state that needs overriding for a certain section of code. There are loads of examples of this pattern in Sitecore development: SecurityDisabler, SiteContextSwitcher and EventDisabler are a few examples you might have come across in your work.

The pattern here is fairly simple. The basic context of an operation has some state value: say "the current user is jdavis". But when your logic is working away it has a need to change that state. Say, "this particular component needs to fetch data that the user jdavis doesn't have access to" so it needs to briefly impersonate an admin user. A particular block of code in your request needs to do this impersonation, but that change should not affect the rest of this request, or any other requests that are happening in the webserver right now.

Having a simple coding pattern which handles this state-change logic in a predictable way is really helpful for us developers - And Sitecore's kernel code does make this pretty simple.

How does it work?

The code you write is very simple. As an example, doing some work as a user with more rights in the system might look like:

void MyApplicationLogic()
{
    // do some stuff as the default user
    // Sitecore's default user state is in place here

    using(new SecurityDisabler())
    {
       // do some work with admin rights
       // Sitecore is bypasing security filtering in this block
    }   

    // finish off back as default user
    // Sitecore's default user state is in place here
}

					

[NB: This is a simple example to read, but it's not really considered good practice. It's better to use UserSwitcher and impersonate a specific account with controlled rights, rather than just discarding security entirely. But while that's an important point, it's not really what I'm focused on here...]

Sitecore have leveraged the IDisposable pattern to make enabling and disabling the change in security here simple to type, and tied to the single block of code that the using() statement encompasses. That's a neat trick to make use of, as it's defining the scope of your change in a visible way.

So under the surface the constructor for SecurityDisabler (or whatever other state change object you're using) will configure the new state. And the C# compiler ensures that however you exit the using block (normally, or because of an exception) the Dispose() method is called, which puts the state back how it was when the block was entered.

There's an interesting wrinkle here, which not everyone spots at first. And that is what state does it need to put back? Internally these context switcher objects maintain a stack of states. Each time the code enters a new switcher block the old state is pushed onto the stack, and each time a the code leaves a block the most recent "old" state is popped off the stack. That allows the code to cope with nesting of these blocks, and other complications without any complex logic.

There's a base type Switcher<TValue, TSwitchType> shared between most of these switcher objects which manages that stack behaviour for the particular bit of state data which is being controlled.

Where's the data?

The other interesting side of this is how these state switcher objects communicate with their underlying systems.

There are two important requirements for this sort of switcher:

  • Firstly, you need to be able to use the behaviour anywhere, but code needs to be able to find its data without you (the developer) having to worry about it. The state can't be tied to the actual switcher object, because the scope of that object is too localised. You can declare new SecurityDisabler() in your controller, but the internal logic of Sitecore's data layer needs to be able to see that state and act on it without knowing anything about where in your request logic the state gets changed.

  • And secondly this code runs as part of an ASP.Net website, where IIS is running multiple requests across different worker threads. The switcher and code and the internal framework it controls must make sure they're using the data for this request and not accidentally picking up state from another parallel request.

The framework neatly solves both of these issues with one data store. Rather than putting the data inside the switcher object itself, it makes use of ASP.Net's HttpRequest.Context.Items collection. That's a per-request data store which exist for the lifetime of the request. It's shared data across the request, accessed via the current HttpContext so any logic can access it, and because it's tied to the specific request being processed there's no threading issue when parallel requests are in flight.

So the switcher logic defines a dictionary key name which is specific to the particular bit of state that it's switching, and stores its stack of states under that key. It uses a key based on the type name for the data being switched - along the lines of typeof(TSwitchType).Name + "Switcher_State". So that name is unique per variety of switcher, but shared between all instances of the switcher object you instantiate in your code.

And when the underlying code of Sitecore's runtime needs to act on one of these states it can call the static CurrentValue method on the switcher type. That lets it find the correct current state by looking at the top of the stack. If there's a value this should be used to override whatever the default state would have been, otherwise the runtime can keep using the default value it had already.

For example, when you access Sitecore.Context.Item the default value to return would be the item that matches the current URL. (Sitecore has parsed your URL using your Site definition, and mapped that to a "page" item in your content tree) But you can use ContextItemSwitcher to change that state if you need to override it. So the logic inside the get accessor for the context item looks vaguely like:

get
{
    // ask the switcher if it has an override value to use
    Item currentValue = Switcher<Item, ContextItemSwitcher>.CurrentValue;

    if (currentValue != null)
    {
        // if it does, return that value
        return currentValue;
    }

    // otherwise, return the current page item
    return _parsedPageItem;
}

					

That way any code in the your solution which accesses the Sitecore.Context.Item value will see the right data based on the switcher.

But it's a pattern of its time...

I don't think we'd implement this in quite the same way these days. Like a number of the approaches in Sitecore's kernel, current good practices have changed a bit. There'd be fewer statics and more DI in a modern variant of this I think. That's certainly the way ASP.Net Core headed with its middleware implementations. This mostly comes down to making automated tests easier - statics aren't great in that scenario because it's more difficult to replace or control them in the context of a test.

But despite this, it's is a pattern that remains useful in your Sitecore codebase. And it's something pretty much every Sitecore implementation makes use of somewhere. And maybe the overall approach still has some uses in non-Sitecore code too?

↑ Back to top