Additional coding best practices
- Last Updated: January 16, 2024
- 2 minute read
- OpenEdge
- Version 12.8
- Documentation
Another important factor in maintaining the integrity of tenant data access is the application’s control of tenant data that is cached in an ABL session’s local data storage. Here is where you must programmatically ensure that any cached tenant-related data is cleared in conjunction with its changing of the database connection’s user identity.
Initialize tenant data stored in global variables or shared session spaces
After correctly setting the user identity, the ABL language engine clears the database connection's buffers. However, if the application code makes a copy of tenant data in global variables or any shared session space (for example, an external file or a caching service), this data could potentially be seen by another tenant that uses the same application.
When copying tenant data outside of the database, the ABL application should ensure that global or shared data is properly initialized prior to, and after, a client request is executed. The following example shows one of many ways where tenant data can be cached and subsequently cleaned up when the user identity of the database connection changes (note code in bold):
define static temp-table ttTenantEmployeeCache like mtdb.EmployeeProfile.
// make a local copy of a tenant table record
buffer-copy mtdb.EmployeeProfile to ttTenantEmployeeCache.
… // run the request
// switch user-id & tenant in database connection
set-db-client(hNewClientID, mtdb).
// clear copies of tenant record data
empty-temp-table ttTenantEmployeeCache.
Ensure client user identity is synchronized across the application
In an ABL application architecture where a single client's requests may execute in different AVM sessions, such as PAS for OpenEdge services, it is possible that a service’s multi-tenant database connection’s user identity may become out of sync with that of the client user identity. This can happen when one PAS for OpenEdge service calls a secondary service and fails to pass the correct user identity. When that condition exists, the secondary service could retrieve and use incorrect tenant data.
To prevent this situation, you should make sure that the fully qualified user
identity (for example, User1@XYZ) that you pass to the secondary
service matches the fully qualified user identity obtained from the database
connection.
The following code snippet shows an example of passing the client identity to the secondary service:
define variable hSecondaryAppServ as handle no-undo.
define variable hCPCopy as handle no-undo.
// create server hSecondaryAppServ.
// hSecondaryAppServ:connect( … ).
// set client’s user-id in the privary service’s multi-tenant database
set-db-client( hClientCP, mtdb).
… // run the request
// call the secondary application server service with the client’s user-id
hCPCopy = get-db-client( mtdb ).
hSecondaryAppServ:request-info:setClientPrincipal( hCPCopy ).
run something on hSecondaryAppServ ().
// clean up deep copy of Client-Principal obtained from the database connection.
if valid-handle(hCPCopy) then delete object hCPCopy.
The following code snippet shows an example of using the passed client identity in a secondary service:
define variable hClientCP as handle no-undo.
hClientCP = session:current-request-info:getClientPrincipal().
If valid-handle( hClientCP ) then do:
// set client’s user-id in the privary service’s multi-tenant database
set-db-client( hClientCP, mtdb).
// clean up deep copy of Client-Principal obtained from the database connection.
delete object hClientCP.
end.