Role Serialization and Deserialization
Learn how to serialize and deserialize Role instances for data storage, transmission, and interoperability.
The Role
class in iamjs provides powerful methods for converting role instances to various formats and recreating them. These methods enhance interoperability and flexibility in managing role data.
Core Serialization Methods
toObject(): GetRoleConfig<T>
Converts the Role
instance to a plain JavaScript object.
const role = new Role({
name: 'editor',
description: 'Can edit and publish content',
config: {
article: { base: 'crudl', custom: { publish: true } },
user: { base: '-r---', custom: { viewProfile: true } }
}
});
const roleObject = role.toObject();
console.log(roleObject);
// Output:
// {
// name: 'editor',
// description: 'Can edit and publish content',
// config: {
// article: { base: 'crudl', custom: { publish: true } },
// user: { base: '-r---', custom: { viewProfile: true } }
// }
// }
toJSON(): string
Serializes the Role
instance to a JSON string.
const roleJSON = role.toJSON();
console.log(roleJSON);
// Output: '{"name":"editor","description":"Can edit and publish content","config":{"article":{"base":"crudl","custom":{"publish":true}},"user":{"base":"r---","custom":{"viewProfile":true}}}}'
Core Deserialization Methods
static fromObject(obj: GetRoleConfig<T>): Role<T>
Creates a new Role
instance from a plain object.
const newRole = Role.fromObject(roleObject);
console.log(newRole.can('article', 'publish')); // Output: true
static fromJSON(json: string): Role<T>
Creates a new Role
instance from a JSON string.
const recreatedRole = Role.fromJSON(roleJSON);
console.log(recreatedRole.can('user', 'update')); // Output: false
Advanced Usage: Custom Transformations
Both toObject
and toJSON
methods accept an optional transform
function for custom processing:
import { encrypt, decrypt } from './cryptoUtils';
// Encrypting role data
const encryptedRoleObject = role.toObject(obj => encrypt(JSON.stringify(obj)));
// Decrypting and creating a role
const decryptedRole = Role.fromObject(encryptedRoleObject, obj => JSON.parse(decrypt(obj)));
Flexible Role Creation: from
Method
The from
method allows creating a Role
instance from any data source:
const externalRoleData = fetchRoleDataFromExternalAPI();
const transformedRole = Role.from(externalRoleData, (data) => {
// Transform external data to Role config format which satisfies the GetRoleConfig<T> type
return {
name: data.roleName,
description: data.roleDescription,
config: data.permissions.reduce((acc, perm) => {
acc[perm.resource] = { base: perm.baseActions, custom: perm.customActions };
return acc;
}, {})
} as GetRoleConfig<typeof role>;
});
This method is particularly useful for integrating with external systems or handling complex data transformations.
Best Practices
- Data Integrity: Always validate the structure of deserialized data before creating Role instances.
- Security: When dealing with sensitive role data, use encryption in conjunction with these methods.
- Version Control: Consider including a version field in your serialized data to handle future schema changes.
- Error Handling: Implement robust error handling when deserializing data from external sources.
By leveraging these serialization and deserialization methods, you can efficiently store, transmit, and recreate role instances, enabling seamless integration with various data storage systems and external APIs.
Detailed Examples of Role Serialization and Deserialization
import { GetRoleConfig, Role, TRoleOptions } from '@iamjs/core';
import crypto from 'crypto';
// Create a complex role for our examples
const complexRole = new Role({
name: 'senior_editor',
description: 'Senior editor with advanced permissions',
meta: {
department: 'content',
level: 'senior',
},
config: {
article: {
base: 'crudl',
custom: {
publish: true,
featureOnHomepage: true,
addComment: true,
},
},
user: {
base: '-r---',
custom: {
viewProfile: true,
sendMessage: true,
},
},
analytics: {
base: '-r--l',
custom: {
exportData: true,
},
},
review: {
base: 'crud-',
custom: {
submitForApproval: true,
},
},
},
});
// 1. toObject() Example
console.log('1. toObject() Example:');
const roleObject = complexRole.toObject();
console.log(JSON.stringify(roleObject, null, 2));
// 2. toJSON() Example
console.log('2. toJSON() Example:');
const roleJSON = complexRole.toJSON();
console.log(roleJSON);
// 3. fromObject() Example
console.log('3. fromObject() Example:');
const recreatedFromObject = Role.fromObject(roleObject);
console.log(
'Can publish article:',
recreatedFromObject.can('article', 'publish'),
);
console.log('Can update user:', recreatedFromObject.can('user', 'update'));
// 4. fromJSON() Example
console.log('4. fromJSON() Example:');
const recreatedFromJSON = Role.fromJSON<typeof complexRole>(roleJSON);
console.log(
'Can export analytics data:',
recreatedFromJSON.can('analytics', 'exportData'),
);
console.log(
'Can delete analytics:',
recreatedFromJSON.can('analytics', 'delete'),
);
// 5. Custom Transformation with toObject()
console.log('5. Custom Transformation with toObject():');
const customTransform = (obj: any) => ({
...obj,
meta: {
...obj.meta,
lastModified: new Date().toISOString(),
},
});
const transformedObject = complexRole.toObject(customTransform);
console.log(JSON.stringify(transformedObject, null, 2));
// 6. Encryption with toJSON()
console.log('6. Encryption with toJSON():');
const encryptData = (data: string) => {
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return { encrypted, key: key.toString('hex'), iv: iv.toString('hex') };
};
const { encrypted, key, iv } = encryptData(complexRole.toJSON());
console.log('Encrypted:', encrypted);
console.log('Key:', key);
console.log('IV:', iv);
// 7. Decryption and fromJSON()
console.log('7. Decryption and fromJSON():');
const decryptData = (encryptedData: string, key: string, iv: string) => {
const algorithm = 'aes-256-cbc';
const decipher = crypto.createDecipheriv(
algorithm,
Buffer.from(key, 'hex'),
Buffer.from(iv, 'hex'),
);
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
};
const decryptedJSON = decryptData(encrypted, key, iv);
const decryptedRole = Role.fromJSON<typeof complexRole>(decryptedJSON);
console.log(
'Decrypted role can feature article on homepage:',
decryptedRole.can('article', 'featureOnHomepage'),
);
// 8. from() Method with External Data
console.log('8. from() Method with External Data:');
const externalRoleData = {
roleName: 'external_reviewer',
roleDescription: 'External content reviewer',
permissions: [
{
resource: 'article',
baseActions: 'r--l',
customActions: { addComment: true },
},
{
resource: 'review',
baseActions: 'crud-',
customActions: { submitForApproval: true },
},
],
};
const transformedRole = Role.from(
externalRoleData,
(data) =>
({
name: data.roleName,
description: data.roleDescription,
meta: {},
config: data.permissions.reduce((acc: any, perm: any) => {
acc[perm.resource] = {
base: perm.baseActions,
custom: perm.customActions,
};
return acc;
}, {}),
} as GetRoleConfig<typeof complexRole>),
);
console.log('Transformed external role:');
console.log('Can read article:', transformedRole.can('article', 'read'));
console.log(
'Can add comment to article:',
transformedRole.can('article', 'addComment'),
);
console.log(
'Can submit review for approval:',
transformedRole.can('review', 'submitForApproval'),
);