App licensing


In order of app developers to restrict the usage of their apps, we provide an API for reading and verifying license files. The licensing mechanism allows the app developer to limit which system the app is run on, by providing a site name that has to match the name in portal.conf as well as the site name in the main site license file.

In addition to that, a configurable set of properties can be added to a license file and these can then be used in the app to enable or disable certain features. Properties contains type information and can store string, integer, float or boolean values.

When a license is issued by the app developer, it is signed using a private key which is kept secret. When the license is loaded into the app it is verified using the corresponding public key which ensures that the license file has been issued by the app developer and that it has not been modified.

Generate license signing keys

To start the license generation process, the app developer has to generate a public/private key pair. The private part of this key pair is used when issuing a license to a customer. This key pair can be used to issue licenses for multiple apps.

You must have a Cantemo installation in order to issue licenses.

This is done by issuing the command:

$ python /opt/cantemo/portal/ generate_license_signing_key

Creating a new license key.
Sucessfully wrote public key to /etc/cantemo/portal/public_license_signing_key.pem
Sucessfully wrote private key to /etc/cantemo/portal/private_license_signing_key.pem

Your keys are now stored in /etc/cantemo/portal/

NOTE: Make sure you create a backup copy of these keys. Without them you will not be able to issue licenses without releasing a new version of your app.

Create a license configuration

The license generation is configured using an xml-file which contains information about which app the license should be generated for and which properties should be configured.

The following is an example of such an xml file.

    <question>Enter number of users (Leave empty for unlimited)</question>
    <question>Should function X enabled?</question>
    <question>How much data can be stored?</question>

Store this file as /etc/cantemo/portal/license-config.xml

You can now issue a license via the following command:

$ python /opt/cantemo/portal/ generate_license
Define license validity date (YYYYMMDD or leave empty for unlimited) 20121231
Please enter the site name:  Portal
Enter number of users (Leave empty for unlimited) (default: -1) 10
Should function X enabled? (y/N) y
How much data can be stored? (default: 23.5)
Wrote license file to testapp.key

The file testapp.key now contains

version = 2
validitydate = 20121231
property_sitename = string:Portal
property_users = int:10
property_candoX = bool:True
property_maxstorage = float:23.5
signature = 539cf058500d139eacf944254aa72bd3adc578c2f83f3c6090e5829cc897f9be75ec8159

This file should be installed as /etc/cantemo/portal/testapp.key on the customer’s system.

The command generate_license takes the following command arguments.




The base directory where license keys and configuration resides.


The filename where you want your license to be stored. Defaults to appname.key in the current directory.


The path to a configuration file. Defaults to license-config.xml in the base directory. If the path starts with a slash, it is interpreted as an absolute path

Programming Examples

In order to verify a license and make use of the properties inside your app, you can call the load_app_license(appname, public_key) function in the portal.licensing.utils module. This will return a portal.generic.general.PortalLicense object which is the object representation of the license file. If no exception is returned, the license file was found and the signature was valid. However, the license can still be expired or invalid for some other reason, so you should check the attribute is_valid. If the license turns out to be invalid, and the function notvalid_reason() gives a localized message that can be presented to the user.

Because the way plugins are loaded in Portal, the license cannot be loaded in the __init__ method. This method is executed too early for the license verification to be in place. This may change in future versions, but currently the best way to load a license file is to call it from the file This file is executed after the boot process is completed but before the first request is served so all required functionality is in place. This can be done by adding the following lines to your

from .plugin import initialize

This will call the function initialize() from your which can be defined as:

def initialize():
  global app_license

      app_license = load_app_license("testapp", "2d2d2d2d2d424547494e205055424c49432
  except Exception, e:
      log.error("Faild to load plugin license: %s", str(e))
      app_license = None

This now makes app_license available in all parts of your app. If you want to check if a user is entitled to use a certain functionality, you can do that with the following code example:

if app_license and app_license.get_property('candoX'):

Apps license API

These are the programming APIs available for the licensing module.



Remove all parts of the site key which aren’t relevant for validating the signature.

portal.licensing.utils.load_app_license(appname, public_key, search_path=None)

Loads an app license and validates it using the public key

Args: * appname - The name of the app as it appears in the section header in the license file * public_key - The encoded public key * search_path - An additional file system path where the function should look for the key

Returns: * A portal.generic.general.PortalLicense object representing the license

portal.licensing.utils.load_product_license(path, sitename=None)

Loads the main product license.

NOTE: This function is only used internally and is called by the bootstrap process. Other users should access settings.SITE_LICENSE_KEY instead. This function’s signature may change in future updates.

Args: * path - The path where a legacy key file can be found * sitename - A sitename to use when verifying the license

Returns: * A portal.generic.general.PortalLicense object representing the site license.


Parses a version 2/3 license file


Parses an individual property value from a version 3 license file.


class portal.generic.general.PortalLicense(sitename='', development=False, users=2, use_i18n=False, validitydate=None, is_valid=True, invalid_reason=None)

The main license class


Returns true if the license expires within 7 days.


Returns the value of a property.

Args: * prop_name - The name of the property

Returns: * The property value casted to its specified type


Returns true if the license has expired.

property is_valid

Is this license valid?


Returns the reason why this license is invalid. Returns None if the license is not invalid


Returns a timedelta object representing the time until this license expires

property version

The version of the license object

class portal.generic.general.PortalLicenseNew(appname, public_key)