I just finished implementing full image branding capabilities for the multi-tenant site management options provided by my platform. One great consequence of the winding down phase of a piece of software development is the reduction of load that attends the end of the line. If your design is one that is conducive to scalability, both in terms of adding new code and functionality to the platform via the API and as well by run time scalability, then as time goes by you should find that it is easier to do more complex things. I was able to implement the branding logic very easily, it only required adding two new columns to the associated site table, the rest of the changes were UI related to enable mutation of the new values.
The branding is a perfect example, being able to allow for distinct managed and secured sites on the same platform required design decisions that were coded into the core API, quite literally several years ago. The main decision was to chose a permission structure that was fine grained and right based NOT group based. I always saw group based permissions systems without an underlying granular right based foundation as asking for trouble. If you have only groups then there will come a point when a desired combination of functionality can not be achieved since no such group atomically defines that functionality. In a right based scheme, rights can be defined to be associated with permissions and then applied to particular class instances. The right designation is orthogonal to the object instance and this allows an exponential relationship between the possible permission combinations that can exceed presently designed needs.
The ability to exceed presently designed need when designing a permission system is important as, when designing a class structure you can't predict how client programmers will use the classes. In order to ensure that rights associated with the permissions vary freely with the desires of client programmers (which you don't even know yet) simply allowing the independent association between instances and rights provides the finest gradation (as fine as the number of rights) possible. This is where choosing the right set of rights comes in, my platform has rights tied to actions that are desired to be performed on instances of a class. The rights include some of the standard rights or permissions that are familiar from unix , (read,write..etc.) but applied to object instances not file system objects. For example, read is analogous to view, write is analogous to edit, the rights are mapped to associated actions via the persistence API. However, because the class objects are managed in a database , additional rights come into play that don't exist in an OS right based system, such as search, import or export. It turns out that some rights have larger scope than the needs of a given class, for example search makes sense for proving permissions to scan collections of a given type which includes all instances of that type, but it makes no sense (currently) within the context of individual instances of a given type. The ability to define search rights for instances of a type are inherited "for free" and allow the implementation of that functionality (for whatever purpose the client programmer should wish) in the future. Now, you might think that this is wasteful but when you realize the permissions are right based, the right only exists and is associated with permissions that are granted to Users. If a permission is not needed, it is not instantiated and does not incur any processing resource to maintain the logic for it in the db in the form of a single row. When a User needs the ability to search a class type, they need only a single permission to allow search for ALL instances of that type. There is an orthogonal relationship between the type, the instance id and the right that allows for a very fine set of permissions , but these are only invoked as needed.
I've found that using a right based permission system has streamlined so many aspects of the design, for example, I can authenticate and authorize Users very efficiently and dynamically modify the UI resources requested by Users to collapse to the limits of the permissions they possess. Thus a fluid UI results that dynamically conforms to the fine grained permissions of the Users requesting the resources. Users that need expansive powers require only single permissions to cover required rights over all instances of type. If they need control over a set of instances of a type they can be given specific permissions for each instance separately. Management of the instances then forms a virtual limit on the number of permissions granted to a given User by virtue of the increasing difficulty with managing many instances. In such cases management of the Users workflow makes it easy to determine if they should have their permission scope increased. So in such a system, each permission is a unique key, and functionality is added by giving a new key. To manage collections of permissions, virtual groups can be created but they compose permissions NOT Users (as in Windows). They are called therefor permission sets, this allows collections of permissions to be managed and granted to or revoked from a User. Permission sets makes setting up right profiles to be granted to Users trivial, the work is done once of defining the right profile by adding the desired class or instance permissions to the set and then the set itself is given to Users, implicitly granting the contained permissions to the User. Allowing permissions to be added just in time has cascaded efficiencies throughout the design. I'll be getting more into the details of the advantages of this system after the site launch.