opcotech/elemo

View on GitHub
docs/ADRs/0016.generating-and-validating-licenses.md

Summary

Maintainability
Test Coverage
# Generating and Validating Licenses

| author       | created at | updated at     | status   |
|:-------------|:-----------|----------------|:---------|
| @gabor-boros | 2023-04-13 | _current date_ | accepted |

## Abstract

On the long term, we want to allow users to host their own instances of Elemo.
This means, we have to make sure that the application is tied to a license, and
faking the license is not possible or at least very hard.

As the license will be a crucial part of the application, we have to make sure
that it is validated upon every application start and while the application is
running.

## Decision

We want to keep the license generation as simple as possible for now, so we can
focus on the other parts of the application, while still making sure that the
license is validated. Keeping that in mind, we are going to use the
[lk library][lk] to generate and validate the license.

The library is capable of generating a license with extra fields that comes
handy in our case as we can put trailing data into the license, which can be
used to further validate the license and allow/disallow certain features.

As of the trailing data, we are going to put the following information into the
license:

* `id`: the license ID, which is an XID, just as in case of the database IDs,
  so we can easily identify the license. Also, we can use the ID to validate
  the creation date of the license, as the ID contains the timestamp of the
  license creation.
* `email`: the email address of the user or organization who requested the
  license.
* `organization`: the name of the organization who requested the license.
* `quotas`: the quotas tied to the license. This is a map of quota names and
  their values.
* `features`: the features enabled for the license. This is a list of feature
  names.
* `expires_at`: the date and time when the license expires.

A decoded license data, at the time of writing, will look like as follows:

```json
{
  "id": "cgrpjvhjr929ohfgsamg",
  "email": "services@opcotech.com",
  "organization": "Open Code Technologies FZC",
  "quotas": {
    "custom_fields": 5,
    "custom_statuses": 3,
    "seats": 5
  },
  "features": [
    "components",
    "custom_statuses",
    "custom_fields",
    "multiple_assignees",
    "releases"
  ],
  "expires_at": "2024-04-12T07:58:22.959795+02:00"
}
```

The license should, ideally, be generated by a license server, however, that
server does not exist yet. Therefore, we have to generate the license manually
upon request. This is solved by the license-generator script, which can be
found in the `tools/license-generator` directory.

Manual validation of the license is also necessary. This is solved by the
license-validator script, which can be found in the `tools/license-validator`
directory.

[lk]: https://github.com/hyperboloide/lk

## Consequences

We will be able to generate and validate licenses, which will be a crucial part
of the application, though the current solution is not scalable, as we have to
generate the license manually.

Also, whenever a new quota or feature is added, we have to update the licenses
themselves. This may be troublesome in the future, but for now, it is
acceptable.

Both issues mentioned above should be solved by a license server, which would
be able to generate and validate the licenses automatically. Also, keeping a
mapping of quotas and features to licenses, so we wouldn't have to update the
licenses themselves.

Due to the nature of how license is used, it is an outlier in the application
in many ways. For example, the license is not pingable, but we have to validate
it and return as part of system health check. Therefore, the `LicenseService`
does implement the `Pingable` interface.

## References

None.