Multi Tenant Support

Two mutually exclusive models are supported, Host Name based or Folder based, you can use one or the other for a given installation. In all cases the first site created is considered the master site, in the database it has the field IsServerAdminSite set to true, and it can create new "child" sites as well as manage child site settings, users, and roles.

There is also a "Related Sites Mode" setting, that can be enabled where the users and roles attached to one site will be used by all the sites in the installation.

The users and roles and other data are tagged with a SiteId and this is used when the data is retrieved. In the case of RelatedSitesMode the users and roles are all tagged with the RelatedSiteId but other data can still be site specific.

Tenant Resolution

Two mutually exclusive models are supported, Host Name based or Folder based (the first folder segment of the url).

Host Name Sites

The tenant is resolved by the host name, ie "somedomain.com" is one host name and "www.somedomain.com" is another host name, you can map one or more host names to any given site. Any host names that are not mapped to a specific site are, mapped to the default site, ie the "master" site. Typically for SEO you don't want duplicate content at different host names, so the notion of a preferred host name is also going to be supported so that if a site is handling multiple host names you can redirect all of them to the preferred one. ie you may prefer "somedomain.com" vs "www.somedomain.com" and you can redirect requests for "www.somedomain.com" to "somedomain.com" if that is your preferred host name.

For a given domain name you can create as many host name DNS "a" records as you like "host1.somedomain.com", "host2.somedomain.com", "cheeseburger.somedomain.com" or "whatever.somedomain.com".

You can create separate new sites using those host names all in a single installation of cloudscribe, you create the site and assign the host name mapping.

Of course the ip address for the host name "a" record must be the ip address where the site is running, and if it is a shared ip address then you must also have a host name header added to the site in the web server, IIS (Windows Internet Information Server) for example, so that the web server will pass the request to the correct site.

This need for creating DNS host names and host header mappings in IIS means that a technical person has to be involved when setting up a new site; non technical people cannot easily provision a new site in this model.

Folder Name Sites

The tenant is resolved by the first folder segment in the url, except for the first site which "must" be a root level site. The fist site created during setup is considered as the "master site" and it does not have any folder mappings assigned to it and it must run as a root level site in order to resolve the child site ids correctly form the first url segment.

ie the master site must work with an url like "somedomain.com/" or "www.somedomain.com/" and then child sites can be created with urls like: "somedomain.com/site2/" "somedomain.com/foo/" "somedomain.com/cheeseburger/" "somedoamin.com/whatever/"

Folder based child sites are easier to provision and are very useful for sub sites that are like sections of one site but with different authoring teams.

Configuration

This example from appsettings.json shows the current default settings. You can easily change or override these settings.

"MultiTenantOptions": {
"Mode": "FolderName",
"UseRelatedSitesMode": "false",
"RelatedSiteId": "00000000-0000-0000-0000-000000000000",
"DefaultNewUserRoles":"Authenticated Users",
"UserPerSiteWwwRoot": "true",
"UserPerSiteThemes": "true",
"UseSharedThemes": "true",
"SiteFilesFolderName": "sitefiles",
"SiteThemesFolderName":"themes",
"SiteContentFolderName":"wwwroot",
"SharedThemesFolderName":"SharedThemes",
"ThemeStaticFilesFolderName":"wwwroot"
}

Mode can be FolderName or HostName or None

If UseRelatedSitesMode were set to true, then all sites will lookup users and roles using the RelatedSiteId, which would typically be the first site created. you can find the siteid in the site list or in the database.

Note the DefaultNewUserRoles property which has "Authenticated Users" which is a role name of a role that will be added to all users as they are created. I recommend keeping that role, but if you want to add additional roles make them semicolon separated.

The other settings are for configuring themes and where site specific user files are uploaded, such as images added in ckeditor.

Should I Host Multiple Customer Sites as Multi-Tenant?

That is of course a business decision that is up to you. Data for all tenants of a given installation are in the same database tables. I do not personally like to mingle data for different customer web sites, so I see multi-tenancy as a useful thing for multiple sites owned by a single customer, with a separate installation per customer being what I recommend.

Managing Sites

When you first setup cloudscribe Core, the first site is created for you automatically, and this site is set in the database with IsServerAdminSite = true, making it the root administrative site. This setting allows members of the Administrators role in the site to create other sites and manage users and roles for other sites. Other sites created from the UI are not flagged as server admin sites so administrators of those sites can only manage the site they belong to.

By default users and roles are separate for each site. It is possible for the same user to register and login to multiple sites but their membership in one site does not grant them access to protected resources in other sites (with the exception of Administrators in the administrative site who as mentioned before can manage other sites, but they do this while logged into the root administrative site, they are not able to login to the other sites without creating a user account in the other sites). There are site settings for each site to enable or disable certain things. For example it is possible to disable self serve registration so no users can register on a site and only administrators can add users to the site.

As you can see in this model the site aka tenant is resolved first based on the hostname or the first folder segment of the request. The user must then log into the site or tenant to access any protected resources and their access to protected resources depends on the configured authorization policies and the user's roles and claims (within the given site) which are compared against the requirements of the policy. The authorization policies are configured in the Startup.cs of the web application where they can be customized. So for example there is a policy protecting the administrative area in cloudscribe Core, it requires the user to be in the Administrators role. While each site will have a role named Administrators, the user must be in the role for the site resolved from the request. Members of the Administrators role of one site are not administrators of other sites, even though other sites also have a role with that name. A user could have a separate account in each site with the same email and they could be added to the Administrators role of each site if you want to make them administrators but users and roles are not shared across sites unless the system is configured with related sites mode. For more information on defining custom authorization policies see the documentation here.

When you create multiple sites with cloudscribe core there is nothing protected by default except the administrative area in each site. If you have a public home controller that allows anonymous access then users can still browse the home page of any site. If you wanted to prevent that then you would have to define a policy to protect the home page controller, then you could decorate the controller class or specific controller actions that you want to protect with a policy attribute:

[Authorize(Policy = "HomePagePolicy")]

You would have to define the policy with that name in Startup.cs something like this:

services.AddAuthorization(options =>
{
	options.AddPolicy(
		"HomePagePolicy",
		authBuilder =>
		{
			authBuilder.RequireRole("Administrators", "Content Administrators");
		});
		// add more policies here as needed

});

If instead of using a standard home controller, you use cloudscribe.SimpleContent, it allows you to create virtual pages and each virtual page has a setting for "AllowedViewRoles". The setting is empty by default, but if you add one or more roles to it then the page would be protected by the required view roles and will not be accessible to anyone who is not a member of the role. The page would also be automatically filtered out of the menu in this case for users who are not members of the needed roles.

What about a user per tenant solution?

This question comes up from time to time, it seems some people want to be able to resolve the user first and then determine the tenant based on the user. Sorry but that is not how it works in cloudscribe Core. What would be possible is to have tenants defined that correspond to users, but then in the root administrative site you would need a custom feature to let the user enter his email address, then you could lookup the tenant in a custom table and redirect the user to the url of the tenant. So you would need a master list in the database to relate the email address of the user to a tenant.

What about a database per tenant solution?

That wasn't really my goal for cloudscribe Core. I wanted something that makes it easy to provision new sites. A database per tenant solution would require manual steps to add new tenants such as creating a new database and database user and keeping a list of the tenants and their database connection strings in a central master database. I don't have any plans to implement that kind of a solution near term but also I am not ruling out doing that in the future. What I am more interested in doing is having a way to import and export tenant data to facilitate moving a tenant to another installation or database. I don't have a timeline for implementing that, what could be done now if I needed to provide a backup of a single tenant is making a backup of the entire database and deleting the other tenant data, then making a backup with just the data of a single tenant. In my work, I favor an installation per customer, so if there are multiple tenants in the installation, they are tenants that all belong to one customer and I don't often need to separate the data later.

If you want to view or submit comments you must accept the cookie consent.