Understanding Isolated Storage
Isolated Storage is key-value-based storage that provides data isolation between extensions. Isolated Storage can be used to store data that must be preserved inside the extension scope, and this data is accessible via AL code by using the IsolatedStorage
data type. The DataScope
option of the IsolatedStorage
data type identifies the scope of stored data in Isolated Storage.
DataScope
is an optional parameter and the default value is Module
. All possible values are listed in the following table:
Member |
Description |
|
It indicates that the record is available in the scope of the app context. |
|
It indicates that the record is available in the scope of the company within the app context. |
|
It indicates that the record is available for a user within the app context. |
|
It indicates that the record is available for a user and specific company within the app context. |
Table 6.2 – DataScope parameter values
To manage data with the IsolatedStorage
data type, you have the following methods:
Method |
Description |
|
This sets the value associated with the specified key within the extension. The optional |
|
This gets the value associated with the specified key within the extension. The optional |
|
This determines whether the storage contains a value with the specified key within the extension. The optional |
|
This deletes the value with the specified key from the Isolated Storage within the extension. The optional |
Table 6.3 – IsolatedStorage data type methods
Isolated Storage is useful to store sensitive data, user options, and license keys.
Let’s consider the following example:
local procedure IsolatedStorageTest()
var
keyValue: Text;
begin
IsolatedStorage.Set('mykey','myvalue',DataScope::Company);
if IsolatedStorage.Contains('mykey',DataScope::Company) then
begin
IsolatedStorage.Get('mykey',DataScope::Company,keyValue);
Message('Key value retrieved is %1', keyValue);
end;
IsolatedStorage.Delete('mykey',DataScope::Company);
end;
From the preceding code, we get the following:
IsolatedStorage.Set
: In the first step, we save, in Isolated Storage, a key calledmykey
with a value ofmyvalue
andDataScope
set toCompany
. The key is visible in the scope of the company within the app context, so no other extensions can access this key.IsolatedStorage.Get
: In the second step, we check whether a key calledmykey
is saved in Isolated Storage withDataScope
set toCompany
. If a match is found (key and scope), the key is retrieved (with theGet
method) and the value is returned in thekeyValue
text variable.IsolatedStorage.Delete
: In the last step, we delete the key for thisDataScope
.
As previously said, you could also use Isolated Storage to save license keys or license details for your extension. The following code shows how to export the records of a table called License
to JSON, then how to encrypt the JSON value, and finally, how to store the encrypted text in Isolated Storage:
local procedure StoreLicense()
var
StorageKey: Text;
LicenseText: Text;
EncryptManagement: Codeunit "Cryptography Management";
License: Record License temporary;
begin
StorageKey := GetStorageKey();
LicenseText := License.WriteLicenseToJson();
if EncryptManagement.IsEncryptionEnabled() and EncryptManagement.IsEncryptionPossible() then
LicenseText := EncryptManagement.Encrypt(LicenseText);
if IsolatedStorage.Contains(StorageKey, DataScope::Module) then
IsolatedStorage.Delete(StorageKey);
IsolatedStorage.Set(StorageKey, LicenseText, DataScope::Module);
end;
local procedure GetStorageKey(): Text
var
//Returns a GUID
StorageKeyTxt: Label 'dd03d28e-4acb-48d9-9520-c854495362b6', Locked = true;
begin
exit(StorageKeyTxt);
end;
local procedure ReadLicense()
var
StorageKey: Text;
LicenseText: Text;
EncryptManagement: Codeunit "Cryptography Management";
License: Record License temporary;
begin
StorageKey := GetStorageKey();
if IsolatedStorage.Contains(StorageKey, DataScope::Module) then
IsolatedStorage.Get(StorageKey, DataScope::Module, LicenseText);
if EncryptManagement.IsEncryptionEnabled() and EncryptManagement.IsEncryptionPossible() then
LicenseText := EncryptManagement.Decrypt(LicenseText);
License.ReadLicenseFromJson(LicenseText);
end;
Here, the License
table is declared as a temporary table. This way, the data is isolated in the calling codeunit.
Related to secret management with Isolated Storage, remember that:
- In Dynamics 365 Business Central SaaS, sensitive data stored in Isolated Storage is always encrypted.
- In Dynamics 365 Business Central on-premises, encryption is controlled by the end user (via the Data Encryption Management page):
- If encryption is turned on, a secret stored in Isolated Storage is automatically encrypted.
- A secret that was inserted while encryption was turned off will remain unencrypted if encryption is turned on.
- If you turn off encryption, the secret will be decrypted.
According to these changes, if you have an extension that works for Dynamics 365 Business Central SaaS and on-premises and you’re using Isolated Storage to store secrets, you need to check whether encryption is enabled (which is always true for SaaS) and then save the secret accordingly.
So, a function that saves a license key to Isolated Storage and works for Dynamics 365 Business Central SaaS and on-premises will be as follows:
local procedure StoreLicense()
var
licenseKeyValue: Text;
begin
if not EncryptionEnabled() then
IsolatedStorage.Set('LicenseKey',licenseKeyValue,DataScope::Module)
else
IsolatedStorage.SetEncrypted('LicenseKey',licenseKeyValue,DataScope::Module)
end;
With the SetEncrypted
method, you can now automatically save a secret by using encryption (no more calls to the Cryptography Management
codeunit).
We have seen how to use Isolated Storage to improve data security in our extensions. In the next section, we’ll see how to create control add-ins.