February 2008 Entries

When trying to access your Report Manager, you might see an Access Denied error with an entry in your application log like:

Failed to execute request because the App-Domain could not be created. Error: 0x80070005 Access is denied.

Thankfully it's a simple one to fix. It's caused because ASP.NET cannot access the Reporting Services folders. I'm not sure what the minimum access permissions are but this was on dev so I simply gave the Network Service full control on my 'ReportManager' and 'ReportServer' folders and that sorted things.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

I was writing an EPiServer plugin recently and it was bothering me slightly that EPiServer only supports .ASCX or .ASPX pages for plugins. What I wanted to do was just drop a single DLL file into the BIN folder of my website. EPiServer 5 does have much better plugin management, true, but it still bothered me. I wanted to have control over the content of my plugin and didn't want to have to package my control along with a DLL.

After rummaging around a bit to see if EPiServer would pick up any overridden render methods if I missed the URL attribute off a plugin (it doesn't) I decided to drop Steve Celius over at EPiServer an email. He said that he'd been having much the same thoughts, and suggested I think about using a custom Virtual Path Provider. I'd already thought about that myself, but Steve suggested the magical twist to it that hadn't occurred to me... he suggested packaging the ASCX/ASPX actually within the plugin assembly DLL itself!

The idea really appealed to me of the assembly both registering the plugin AND providing the control as well, and so I started my investigation with some links that Steve sent me of others that had been doing it in general ASP.NET coding. What I needed to do was work out a way to make it work cleanly with EPiServer.

It turns out that it is actually incredibly easy. So easy, in fact, that I can give you a step-by-step guide to writing a simple single-assembly plugin. Note that I'm using a user control (.ASCX) in the Action Window rather than a page (.ASPX) but I imagine the principles would still broadly apply.

Before we start, I want to give a major shout out to Kurt Harriger and his post over at CodeProject for doing all of the legwork around a custom VPP for assembly-embedded resources. To start with, lets do a step-by-step so that we have the 'infrastructure' in place:

  1. Open up VS.NET 2005
  2. Create a new class library project called SingleAssemblyPlugin (I'm using C# for this article)
  3. Set the default namespace of the project... I'm using BusinessDecision.EPiSample.SingleAssemblyPlugin but you can use anything you like - just make sure you amend your code as appropriate
  4. Delete the Class1.cs file... the namespace won't be set correctly and I've never seen the point of it :)
  5. Add a reference to System.Web and the EPiServer.dll file (you'll find a copy in the BIN folder of your EPiServer website)
  6. Create a new class file called AssemblyResource.cs
  7. Cut-and-paste the AssemblyResourceProvider and AssemblyResourceVirtualFile classes from this CodeProject article from Kurt Harriger into your new class
  8. Add a 'using' statement for System.Web and System.Web.Hosting to your class file

At this point we have a class library all ready to go and ready to start embedding resources. We have a drawback though. We can create a user control in the class library but it will just get compiled and when you distribute the DLL it won't be present as a resource. The way to get around this is to add the user control but then to set 'Build Action' property of the .ASCX file to 'Embedded Resource'. This will mean it is embedded as a resource within the assembly and not compiled.

The other snag is that the way that Visual Studio figures out the code behind file is to look at the 'CodeBehind' or 'CodeFile' attribute of the .ASCX file to determine the .ASCX.CS file. As we aren't using the designer, we don't need to worry about the attribute. Just delete it. Scary I know, but if we don't Visual Studio will complain. The compiler won't throw an error because  we set the user control to be an embedded resource rather than compiled.

That all might sound complicated but in reality it's very simple to implement. Let's see this step-by-step:

  1. Add a new user control to the class library called HelloWorld
  2. In the HelloWorld.ascx file, just put in some 'Hello World' text so that we can be sure it's working when we use it later
  3. Also in the HelloWorld.ascx file, delete the CodeFile (or CodeBehind attribute)
  4. On the property page for HelloWorld.ascx change the Build Action to 'Embedded Resource'
  5. In HelloWorld.ascx.cs, add the following snippet just before the class declaration
   1: [EPiServer.PlugIn.GuiPlugIn(DisplayName = "HelloWorld",
   2: Description = "Hello World!",
   3: Area = EPiServer.PlugIn.PlugInArea.ActionWindow,
   4: Url = "~/App_Resource/SingleAssemblyPlugin.dll/BusinessDecision.EPiSample.SingleAssemblyPlugin"]

Just one thing left to do. Everything is there ready to go and EPiServer would pick up the presence of the plugin, but when you tried to use it the EPiServer site would throw an error about not being able to find the user control. This is because it is trying to load the user control via the URL like any other control - it doesn't know about our custom VPP. We therefore need to tell it about the custom VPP. Kurt Harriger's article gives one way to do this via adding a reference and putting some code in the Global.asax.cs file of our website. That works, and you could also use .NET-style 'late binding' instead, but Steve Celius told me that there is a neater way to do it using some built-in EPiServer VPP support (thanks also to Mats Hellström for the original code).

Doing this again step-by-step:

  1. Add a new class to the class library called AssemblyResourceRegistration
  2. Add 'using' statements for System.Reflection, System.Web and System.Web.Hosting
  3. Delete the automatically created class declaration
  4. Copy-and-Paste in the class code below
   1: [EPiServer.PlugIn.PagePlugIn("Vpp Initializer Plugin", "")]
   2: public class VppInitializer
   3: {
   4:     public static void Initialize(int optionFlag)
   5:     {
   6:         System.Diagnostics.Debug.Write("VppInitializer.Initialize called with: " + optionFlag.ToString());
   7:  
   8:         if (HttpContext.Current == null)
   9:         {
  10:             // Running from the scheduler, skip registration
  11:  
  12:             System.Diagnostics.Debug.Write("VppInitializer called without HttpContext. Exiting");
  13:  
  14:             return;
  15:         }
  16:  
  17:         // Register
  18:  
  19:         Assembly lateBoundAssembly = System.Reflection.Assembly.LoadFrom(HttpContext.Current.Server.MapPath(@"\bin\SingleAssemblyPlugin.dll"));
  20:  
  21:         HostingEnvironment.RegisterVirtualPathProvider((VirtualPathProvider)lateBoundAssembly.CreateInstance("BusinessDecision.EPiSample.SingleAssemblyPlugin.AssemblyResourceProvider"));
  22:     }
  23: }

What this code is doing is adding our custom VPP to the 'chain' of Virtual Path Providers so that it will be evaluated whenever there is a call for a virtual path (which LoadControl does). Note again that you'll need to change the name of the namespace to the one you want.

Now compile your class library and drop the DLL into the BIN folder of your EPiServer site. Fire up your site, and login to the Edit Mode. Open the Action Window and you should have a new entry!

And if we click the new entry we see:

And it is that simple! We've just built a single-assembly plugin, dropped it in and fired it up. The scope for simple single-assembly plugins is really limitless, and there are a number of advantages to doing things this way.

  • One of the nice things about using this technique rather than a Server Control is that it's a much nice development environment. If you've ever written a Server Control, then you will know that they often end up as piles of code. This way, you can even write the user control as part of your site before abstracting it later and take advantage of all the IDE tools.
  • Because the assembly is 'late-bound' and registers itself, you can just drop an updated plugin into the BIN folder of your website

There is one minor thing I don't like so much about this technique though. The VPP is checked with every request for a virtual path. Now, as you can see in the code it is doing a string comparison. I can't see that this would perform badly but it's one more thing per page hit and on a huge site it might have some tiny impact. I think this is unavoidable but hopefully negligible.

Any other feedback or improvements on this technique would be appreciated!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

Just a couple of quick gotchas when using EPiServer manager:

EPiServer Manager 1.2x to 1.3.3.0 upgrade: When you try to install the new version you get an option to repair or remove. You actually need to remove and then run the install again. Repair doesn't actually upgrade it.

EPiServer manager 2.0.19.4 install of site: For some reason when you try to install into your inetpub folder it insists on using c:\inetpub\MyEPiServer, as below:

If you want to use MyEPiServer that's fine, but if you don't then it's greyed out which is a bit unhelpful. Thankfully it's just a bug which can be got around. Type anything into the Virtual directory field and delete it again and it will actually show the directory you are trying to install in, for example:


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

In my last post we discussed the DACL and how to get the Access List from a Directory Entry object. We finished the post by grabbing our (D)ACL as an AuthorizationRuleCollection.

In this post we are going to look at what that collection contains; a set of AuthorizationRule objects. Just to make things a little more interesting, the AuthorizationRuleCollection can contain two types of rule - Access Rules (for the DACL) and Audit Rules (for the SACL). As we are looking at the DACL rather than the SACL we will be looking at a type that is inherited from the AuthorizationRule called an AccessRule. If you were dealing with Audit Rules you would use the AuditRule type but we're not looking at those - we know that we only have AccessRule objects because we called the GetAccessRules method. If we wanted the SACL, or Audit Rules, then we'd call... yes you guessed it... GetAuditRules.

So we have our AccessRule objects. What does an AccessRule actually consist of? You can look on MSDN but frankly that won't help you too much. There isn't many properties or methods. This is because we actually want to deal with an object that is inherited from THAT one! Specifically, we want to deal with ActiveDirectoryAccessRule objects. So lets put this in perspective. We have our AuthorizationRule collection, but what do we actually want to work with?

System.Object - everything inherits from this
 System.Security.AccessControl.AuthorizationRule - what we started with
  System.Security.AccessControl.AccessRule - we know we want a DACL
   System.Security.AccessControl.ObjectAccessRule - adds some stuff, don't worry about this
    System.DirectoryServices.ActiveDirectoryAccessRule - Bingo!

This is one of the real gotchas of developing with ACLs because unless you know to jump to a class in another namespace you could get as far as an ObjectAccessRule and get stuck there. Thankfully, once you know you know and it's not really as bad as all that. Yes, phew :)  All we need to do is just cast our AuthorizationRule all the way to an ActiveDirectoryAccessRule:

foreach (ActiveDirectoryAccessRule rule in aclRules)
{
    // do stuff
}

So now, at last, we have our rule and we can have a poke around it. Now bear with me on this one because it's a little cryptic, but you need to know what every part of a rule does. 


BREAKOUT:

Here's a little something for you to consider as this point. Any ACL editor you write should be able to deal with EVERY aspect of an ACL. Why? Well, it's down to the way the API works.

As we'll see later, the API provides ways to delete and add rules, but not to edit rules. You cannot just edit a particular attribute of an ACL without having to worry what all the rest do because there is no method to do that. This means that if you are working on existing rules, you effectively have to rebuild them from scratch with any appropriate change and then re-add them. If you start ignoring stuff on the existing rule when you rebuild it from scratch then you'll lose information.

For example, lets say you have an ACL that says "Allow John Smith to Add, Remove and List the Contents of all types of Foobar objects but only if they are direct children of the current object".  If you miss one of the ACL properties, you could miss the fact that this only applies to Foobar objects. You could then end up with "Allow John Smith to Add, Remove and List Contents of all types of objects that are direct children of the current object." You can see that this could lead to all sorts of undesirable consequences!

An interesting aside is that the permissions editor for Active Directory / ADAM (ADSIEdit Security) does not fully support all aspects of the Active Directory permissions model. Yes, I know thats crazy. It is possible though to do some clever things with inherited permissions and objects they apply to that will be wiped out if you use the security tools. They are quite advanced permissions and it's unlikely you would come across them. I guess the tools MS provide cover 99% of cases and they figured that was good enough, and besides they probably doubted anyone would be doing anything that advanced programatically. 


So what can our rule contain?

  • AccessControlType - Enumerator that determines whether this is an Allow or Deny rule. It's a bit clunky, but lets say you want to Allow A and B rights to an object and Deny X and Y rights. You will actually need two rules to do this, one for the Allow and one for the Deny. Note also that if you Allow A and B rights and Deny B and C, for example, then B will be denied as the denial ALWAYS overrides.
  • AccessMask - This is the access mask we set to do the rule search earlier, so it's not really that important other then for interest
  • ActiveDirectoryRights - OK, this is the meaty bit :)  We'll give this better coverage later
  • IdentityReference - This is where the identity of the user/group to whom this ACL is applied is stored. If you remember on our previous post we discussed SIDs and NT Accounts - well this is where that information will be.
  • InheritanceFlags - Sets whether inheritance applies to containers or leaves... we'll always use Container or a bitwise of Container and Leaf
  • InheritanceType - Determines how far down the tree below this object the ACL applies
  • InheritedObjectType - this is the type of object that can inherit this rule. For example, you could say 'only allow Computer objects to inherit this Rule'. It is stored as an object GUID and we'll show how to work out what it is later.
  • IsInherited - shows whether this is an inherited rule. If it is, then chances are we'll show it in our ACL editor as a read-only rule as it should be changed up the tree where it actually originates
  • ObjectFlags - info flag that we shouldn't need to worry about unless we build in some checking code
  • ObjectType - an object to which this rule applies, which in reality will be some kind of property in the AD Schema. For example if this is a read permission rule then this could say 'allow reading of property X'
  • PropagationFlags - shouldn't ever need to worry about this as it's related to inheritance but the inheritance is more important

Even though I've explained the properties a bit, you're probably not much better off than when you started. I think it best to give an example of a rule and we can see where the various attributes fit into things.

Lets say you have a rule like the following:

"Allow John Smith to Read or Change the displayName property of all objects of type User on every child object of this DirectoryEntry, but not this entry itself"

In terms of our object properties, we say the following:

"[AccessControlType: Allow] [IdentityReference: SID for John Smith] [ActiveDirectoryRights: Read and Change] [ObjectType: Object GUID for displayName] [InheritedObjectType: Object GUID for User] [InheritanceType: Descendents]"

By this point we should have a basic understanding of the kind of information that we have on an ActiveDirectoryAccessRule, which is the basic building block for an ACL. In my next post I'll look at a bit more detail at what information some of these properties actually contain and where we can find what they mean.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

 Firebrand logo

Well I'm back from my holidays to South Africa, and trying my best to adjust to a British winter after enjoying Summer on the Indian Ocean :)

As a company we've been using the Training Camp for quite a while (MOSS, .NET exams, CRM) and it recently rebranded itself as Firebrand. Well worth a look if you have any training requirements, especially if you need a real kickstart to a technology for an urgent need.

And no matter how well you plan, you just know that your next project will be that new version of the server technology that you didn't expect to come just yet!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist