Multitenancy

Objective


SOAJS gives you the ability to create multiple tenants and assign product packages to them. A tenant represents a client. So to apply the concept of having one service serve several tenants differently, such as URAC v2, we need to configure our service to use multitenancy, create tenants and assign keys to them. And when making calls to the APIs of that service, specify the key of the tenant in those requests. Example03 demonstrates the usage of several tenants and in some cases, several applications of a tenant with the same service.


This space will show you:

  1. Tenants
  2. Applications
  3. Code Examples



Tenants


Tenants can be created and configured using the Dashboard of SOAJS. Tenants have unique identification codes and optional oAuth configuration. Checkout Example02 to learn how to protect a service using oAuth.


var test = {
    "_id": ObjectId("10d2cb5fc04ce51e06000001"),
    "oauth": {
        "secret": "shhh this is a secret",
        "redirectUri": "",
        "grants": ["password", "refresh_token"]
    },
    "code": "test",
    "name": "Test Tenant",
    "description": "this is a description for test tenant",
    "applications": [
        //...
    ]
};

The above snippet represents one tenant's records that is stored in the database core_provision under tenants. The tenant is called Test Tenant, has oAuth security information and has an application array.



Applications


{
    "product": "PROD1",
    "package": "PROD1_PCK1",
    "appId": "30d2cb5fc04ce51e06000001",
    "description": "this is a description ...",
    "_TTL": 604800000,
    "keys": [
        {
            "key": "d1eaaf5fdc35c11119330a8a0273fee9",
            "extKeys": [
                {
                    "expDate": 1426074981752,
                    "extKey": "aa39b5490c4a4ed0e56d7ec1232a428f77...",
                    "device": {
                        "browser": "ff"
                    },
                    "geo": {
                        "ip": "127.0.0.1"
                    }
                }
            ],
            "config": {
                "dev": {
                    "urac": {
                        "hashIterations": 1024,
                        "seedLength": 32
                    }
                }
            }
        }
    ]
}

The above snippet represents an application in a tenant that uses PROD1_PCK1 package and inherits the ACL from that package. Since PROD_PCK1 provides access to the URAC service, this application can now use the service and give it some custom configuration as shown in the config.urac entry inside the keys array.
Every application needs keys to function, without those keys the application by itself does nothing. Once a key is added to an application, the application can then provide custom configuration to a service and can dispose of external keys for that particular key.
External Keys are used in requests made to a service; they are secured using geo and device security and have an expiry date. Think of them as license keys. When used, these external keys are checked for device and geo location, and if valid, they are matched with the key represent. Now that the key has been identified, its configuration is loaded and in the cases above, for URAC the tenant configuration for this key is passed to the service for usage. Tenant Key Security is explained in depth in the section named Security.


{
    "product": "PROD1",
    "package": "PROD1_PCK2",
    "appId": "54d2cb5be64ce51e06000005",
    "description": "this is a description ...",
	"scope": {
    	"acl": {
			"dev": {
        		"urac": {
					"2": {}
				},
        		"example02": {
					"1": {}
				},
        		"example03": {
					"1": {}
				}
			}
    	}
	},
    "_TTL": 604800000,
    "keys": [
        {
            "key": "e5eaaf5fdc35c164319330a8a0273feb6",
            "extKeys": [
                {
                    "expDate": 1426074994350,
                    "extKey": "7bc1e66d003a3b2acfce1557cbda7320...",
                    "device": {},
                    "geo": {}
                }
            ],
            "config": {
                "dev": {
                    "urac": {
                        "hashIterations": 1024,
                        "seedLength": 32
                    },
                    "example03": {
                        "tenantName": "My Test Tenant"
                    }
                }
            }
        }
    ]
}

The above snippet is another application in a tenant that uses PROD1_PCK2 package, but this time it overrides the ACL of that package with additional permissions. The application grants access to URACExample02 and Example03 and in the key configuration, both URAC and Example03 have custom configuration. Keep in mind that the ACL configuration follows the same syntax whether used in packages or in applications. You can learn more about ACL in the Access Levels section.



Tenant Application Configuration

When loading the configuration of a tenant, the environment is checked first then the configuration is extracted from it. In the above sample, the configuration is stored under dev environment. This means that if the application is running in development environment based on the environment configuration, when invoked, only this configuration is loaded for the URAC service for this given key. If the application is running in another environment, this configuration is not used. SOAJS is multienvironment and that feature is explained in depth in Multi Environment section.

The loaded tenant configuration, will exclude the environment. If you look at the above sample code, both urac and example03 are in dev environment but when this configuration is loaded, dev is removed and its content is loaded only resembling something like the below:

 "urac": {
    "hashIterations": 1024,
    "seedLength": 32
},
"example03": {
    "tenantName": "My Test Tenant"
}

This configuration is then passed on to the service and is accessible via req.soajs.serviceConfig.urac and req.soajs.serviceConfig.example03. The service does not need to know what other configuration this tenant has for other environments. The service only uses what it needs for the environment it is currently running in.


Tenant Full Sample


Grouping the above three snippets will result in the below tenant record:

var test = {
    "_id": ObjectId("10d2cb5fc04ce51e06000001"),
    "oauth": {
        "secret": "shhh this is a secret",
        "redirectUri": "",
        "grants": ["password", "refresh_token"]
    },
    "code": "test",
    "name": "Test Tenant",
    "description": "this is a description for test tenant",
    "applications": [
        {
            "product": "PROD1",
            "package": "PROD1_PCK1",
            "appId": ObjectId("30d2cb5fc04ce51e06000001"),
            "description": "this is a description ...",
            "_TTL": 604800000, // 7 days in milliseconds
            "keys": [
                {
                    "key": "d1eaaf5fdc35c11119330a8a0273fee9",
                    "extKeys": [
                        {
                            "expDate": new Date().getTime() + 604800000,
                            "extKey": "aa39b5490c4a4ed0e56d7ec1232a428f77...",
                            "device": {
                                "browser": "ff"
                            },
                            "geo": {
                                "ip": "127.0.0.1"
                            }
                        }
                    ],
                    "config": {
                        "dev": {
                            "urac": {
                                "hashIterations": 1024, //used by hasher
                                "seedLength": 32 //used by hasher
                            }
                        }
                    }
                }
            ]
        },
        {
            "product": "PROD1",
            "package": "PROD1_PCK2",
            "appId": ObjectId("54d2cb5be64ce51e06000005"),
            "description": "this is a description ...",
            "acl": {
                "dashboard": {
                    "oauth": [{
                        "version": "1",
                        "get": ["Guest"],
                        "post": ["Guest", "Tokenization"],
                        "delete": ["Tokenization"]
                    }],
                    "urac": [{
                        "version": "2",
                        "post": ["Guest Password Settings"],
                        "get": ["Guest Password Settings", "Guest Email Validation"]
                    }],
                    "dashboard": [{
                        "version": "1",
                        "post": ["Private Tenant ACL"]
                    }]
                }
            },
            "_TTL": 604800000, // 7 days in milliseconds
            "keys": [
                {
                    "key": "e5eaaf5fdc35c164319330a8a0273feb6",
                    "extKeys": [
                        {
                            "expDate": new Date().getTime() + 604800000,
                            "extKey": "7bc1e66d003a3b2acfce1557cbda7320...",
                            "device": {},
                            "geo": {}
                        }
                    ],
                    "config": {
                        "dev":{
                            "urac": {
                                "hashIterations": 1024, //used by hasher
                                "seedLength": 32 //used by hasher
                                // more configuartion
                            },
                            "example03": {
                                "tenantName": "My Test Tenant" //specific tenant info for service
                                // more configuartion
                            }
                        }
                    }
                }
            ]
        }
    ]
};

Using both Productization and Multitenancy and with the wide combination offered by the Access Levels, SOAJS gives you limitless power to build Service-Oriented Applications that serve multiple tenants, and configure their access as you desire with very little effort and time as well as the ability to deploy that service in Multiple Environments.