Mastering `canRead()` and `canWrite()`: Your Guide to Secure GlideRecord Operations in ServiceNow
As ServiceNow developers, we’re often tasked with crafting custom solutions that extend the platform’s capabilities. Whether it’s automating a complex workflow, integrating with external systems, or simply refining user interactions, our scripts are the backbone of innovation. But with great power comes great responsibility, especially when dealing with data. How do we ensure our scripts respect the platform’s robust security framework? How do we build applications that are not only functional but also inherently secure and user-permission-aware?
Enter canRead() and canWrite() – two seemingly simple yet profoundly powerful methods within the GlideRecord API. These aren’t just obscure functions; they are your frontline defenders, ensuring that every data operation you perform aligns with the platform’s Access Control Rules (ACLs). In this detailed guide, we’ll peel back the layers, moving beyond basic definitions to understand the true impact, practical applications, and troubleshooting nuances of these essential methods.
The World of Glide APIs: Your ServiceNow Toolkit
Before we dive deep into our specific methods, let’s take a moment to appreciate the ecosystem they belong to. ServiceNow’s Glide APIs are the developer’s Rosetta Stone, allowing us to communicate directly with the platform’s core functionalities. Instead of wrestling with raw SQL queries, these APIs provide an object-oriented, JavaScript-friendly interface to interact with the database and other platform components.
Glide API Overview – A Quick Refresher
ServiceNow developers frequently leverage Glide APIs to customize applications and alter default behaviors. These APIs offer unparalleled flexibility, enabling us to perform complex operations – from simple data retrieval to intricate integrations – all within the platform’s governed environment. They abstract away the underlying database complexities, letting us focus on business logic.
Glide APIs are broadly categorized into Client-side and Server-side. Client-side APIs (like GlideForm or GlideAjax) interact with the user interface, responding to user actions and updating form fields. Server-side APIs, our focus today, operate directly on the server, handling database transactions, business logic, and integrations. Among these, GlideRecord stands tall as the most vital Server-side API.
GlideRecord: Your Database Commander-in-Chief
If you’ve spent any time scripting in ServiceNow, you’ve undoubtedly encountered GlideRecord. It’s more than just a class; it’s the primary gateway for server-side scripts to interact with the ServiceNow database. Think of it as your trusted assistant for all database operations, translating your JavaScript commands into the necessary SQL queries behind the scenes.
What Exactly is GlideRecord?
At its core, GlideRecord is a specialized JavaScript class designed to perform standard CRUD (Create, Read, Update, Delete) operations on ServiceNow tables. Instead of writing raw SQL, which is intentionally restricted for direct interaction in ServiceNow for security and abstraction reasons, you instantiate a GlideRecord object for a specific table. This object then allows you to query records, insert new ones, modify existing data, or delete unwanted entries.
It’s incredibly powerful because it understands the structure of your tables – both rows and columns – and handles the complexities of database interaction for you. This abstraction not only simplifies development but also enhances security and consistency across the platform.
Why GlideRecord and Not Direct SQL? The ServiceNow Philosophy
You might wonder why ServiceNow prevents direct SQL interaction. The reasons are multifold and crucial for platform integrity:
- Security: Direct SQL injection is a significant vulnerability. By abstracting database access through
GlideRecord, ServiceNow maintains tighter control, minimizing security risks. - Abstraction: Developers don’t need to be SQL experts.
GlideRecordprovides a higher-level, more intuitive way to interact with data. - Platform Independence: ServiceNow can change its underlying database technology without breaking your scripts. Your
GlideRecordcode remains functional because it interacts with the platform’s API, not directly with the database engine. - ACL Enforcement: Crucially,
GlideRecordmethods are designed to respect Access Control Rules (ACLs). When you use methods likequery()orupdate(), the platform inherently checks if the current user (or system context) has the necessary permissions. This built-in security is a cornerstone of ServiceNow’s architecture.
Key Characteristics of GlideRecord (Beyond the Basics)
- Most Common API: If you’re writing server-side code, you’re almost certainly using
GlideRecord. It’s fundamental. - Running from Server Side: All
GlideRecordoperations execute on the ServiceNow server, never directly in the user’s browser. - Used to Generate SQL Queries: Your JavaScript commands are translated into optimized SQL queries behind the scenes, ensuring efficient database interaction.
- Perform CRUD Operations: This is its bread and butter – creating, reading, updating, and deleting records.
- Record-Level Interaction: While you interact with tables,
GlideRecordprimarily operates on individual records or sets of records that match your query. - Built-in Security Context: A key characteristic that often goes unsaid –
GlideRecordmethods, by default, execute within the security context of the current user (or system process). This is wherecanRead()andcanWrite()become indispensable.
A Word of Caution: Best Practices and Troubleshooting for GlideRecord
GlideRecord is powerful, but like any powerful tool, it demands respect and careful handling. A common mistake is deploying untested scripts directly into production. An incorrectly constructed encoded query, for instance, might return an unintended dataset. If you then run an insert(), update(), or especially a deleteMultiple() method on bad query results, you could face significant data loss or corruption. Always, always, test your queries on a non-production instance before deploying them into a production environment.
Troubleshooting Tip: If your GlideRecord script isn’t behaving as expected, particularly if it’s not finding records or modifying them, start by examining your query. Use gs.print() or gs.log() to output the generated SQL (gr.getEncodedQuery() can be useful here, or even printing the count of records found) and compare it against your expectations. More importantly, consider the security context: are the records you’re trying to access or modify even visible or writable to the user (or system) running the script?
Navigating the GlideRecord Method Maze
The GlideRecord API is vast, offering hundreds of methods to precisely control how you interact with your data. From adding complex queries to managing attachments, there’s a method for almost every scenario. While we won’t list them all here, it’s helpful to understand the scope.
A Glimpse at the Powerhouse Methods
GlideRecord methods can be broadly grouped:
- Querying Methods:
addQuery(),addEncodedQuery(),setLimit(),orderBy(),next(),get(),getRowCount(), etc., used to find and navigate records. - Modification Methods:
insert(),update(),deleteRecord(),deleteMultiple(),initialize(), etc., used for CRUD operations. - Field Interaction Methods:
getValue(),setValue(),getDisplayValue(),isValidField(),getElement(), etc., for interacting with individual field data. - Utility & Security Methods:
isValidRecord(),isNewRecord(), and our stars today:canCreate(),canRead(),canWrite(), andcanDelete().
The Access Control Quartet: `canCreate()`, `canRead()`, `canWrite()`, `canDelete()`
These four methods are the unsung heroes of secure scripting. They provide a programmatic way to check, in advance, if the current user’s permissions (as determined by ACLs and roles) allow for specific operations on a given record or table. While all four are critical, canRead() and canWrite() are particularly frequently used for managing data visibility and modification.
Unveiling `canRead()` and `canWrite()`: Your Security Sentinels
canRead() and canWrite() are fundamental methods for determining if the current user has the necessary permissions to interact with a record based on the platform’s Access Control Rules (ACLs). They don’t just tell you “yes” or “no”; they implicitly evaluate all the complex logic configured in your ACLs – roles, conditions, and scripts – to give you a definitive answer.
Why Are `canRead()` and `canWrite()` So Crucial?
Imagine building a custom portal where users can view and update their own requests. Without a programmatic way to check permissions, you might display fields they shouldn’t see or allow them to click an “Edit” button only to hit a brick wall of unauthorized access errors. These methods prevent such scenarios, allowing you to:
- Implement Secure and User-Permission-Aware Scripts: Proactively check permissions before attempting a read or write operation, preventing errors and ensuring compliance with security policies.
- Enhance User Experience (UX): Dynamically hide elements, disable buttons, or tailor information based on what a user is actually allowed to do, avoiding frustration.
- Prevent Unauthorized Data Access/Modification: Safeguard sensitive data by ensuring scripts adhere strictly to defined ACLs.
- Streamline Troubleshooting: Quickly diagnose ACL-related issues by verifying permissions in a controlled environment.
Deep Dive into `canRead()`: Permission to See
The canRead() method is your go-to for checking if the current security context (typically the logged-in user, but could be a system user in a Business Rule) is permitted to read records from a specific table, or even a specific record.
What `canRead()` Does
When you call gr.canRead(), the ServiceNow platform performs a comprehensive evaluation of all ‘read’ ACLs defined for the table associated with your GlideRecord object. This includes:
- Table-level ACLs: Checks if the user has read access to the entire table.
- Field-level ACLs: While
canRead()on theGlideRecordobject primarily evaluates table access, it does take into account if the user has any read access to any part of the record. If all fields are blocked by field-level ACLs, the record itself might effectively be unreadable. - User Roles: Are the required roles present?
- Conditions: Do the specified conditions in the ACL evaluate to true for the current record?
- Script Logic: Does any script within an ACL return true?
If all evaluated ACLs collectively grant read permission, canRead() returns true. Otherwise, it returns false.
Practical Example: Checking Read Access on Incident Records
Let’s look at the basic usage provided in the reference, and then expand on it. This script would typically run in a server-side context, such as a Background Script, Business Rule, or Script Include.
var inc = new GlideRecord('incident');
gs.print(inc.canRead());Result: If the user running this script (e.g., an ‘admin’ or ‘itil’ user) has roles that permit reading incident records, the output will be true. If the user lacks appropriate roles (e.g., a user with no roles trying to access an ITIL-restricted table), it would be false.
Beyond the Basic: When `canRead()` Becomes More Complex
Specific Record Context:
If you’ve queried a specific record,
canRead()will check ACLs against that particular record’s field values.var incidentGR = new GlideRecord('incident'); incidentGR.get('a4f00996c611227001a1c432764f6974'); // Replace with a valid Incident sys_id if (incidentGR.isValidRecord()) { gs.print('Can read incident ' + incidentGR.number + '? ' + incidentGR.canRead()); } else { gs.print('Incident record not found or not accessible.'); }Here, the ACLs might have conditions like “Active is true” or “Caller is dynamic user (me)”.
canRead()will evaluate those against the retrieved record.Field-level ACLs and Nuance:
It’s important to understand that
inc.canRead()checks if the *record* can be read. A user might have table-level read access, but specific fields might be hidden by field-level read ACLs. IfcanRead()returnstrue, it means the user can read the record, even if some fields are subsequently blanked out due to field-level ACLs. To check individual field readability, you’d typically use a combination ofGlideRecordand client-side checks (via GlideAjax) or evaluate `g_form.isMandatory(‘field_name’)` on the client.Implicit vs. Explicit:
ACLs combine roles, conditions, and scripts.
canRead()implicitly understands and executes this entire logic. You don’t need to manually checkgs.hasRole('itil'), thengr.active == true, then some custom script;canRead()does it all for you.
Troubleshooting `canRead()` Issues
If your script attempts to read a record and fails silently (or logs an error when it reaches a subsequent operation), and you suspect an ACL issue, canRead() is your first diagnostic tool. Here’s how to approach it:
- Run in Background Script: Execute your
canRead()check in a background script, logged in as the user experiencing the issue (or impersonating them). This immediately tells you if the problem is permission-related for that user. - Verify Roles: Does the user have all the roles required by the ‘read’ ACLs for the table/record?
- Check ACL Conditions: Are there conditions on the ACL that the record itself doesn’t meet (e.g., “State is New,” but the record is “Closed”)?
- Debug ACL Scripts: If an ACL has a script, temporarily add
gs.log()statements within that script to see what it’s evaluating and returning. - Use the ACL Debugger: In ServiceNow, navigate to
System Security > Debugging > Debug Security. This will display detailed ACL evaluation results for every interaction, showing you exactly which ACLs are being matched or denied. This is invaluable.
Deep Dive into `canWrite()`: Permission to Modify
Similar to canRead(), the canWrite() method is crucial for determining if the current security context has the necessary permissions to modify records. This is vital before attempting any update() or delete() operations.
What `canWrite()` Does
When you call gr.canWrite(), the platform evaluates all ‘write’ ACLs applicable to the table and potentially the specific record. This evaluation includes:
- Table-level ACLs: Checks if the user has write access to the entire table.
- Field-level ACLs: Similar to read,
canWrite()on theGlideRecordobject checks if the user has permission to write to *any* field on the record. If all fields are blocked by field-level write ACLs, the record might effectively be unwritable. - User Roles: Are the necessary roles present for modification?
- Conditions: Do the ACL conditions (e.g., “Active is true,” “Assigned to is me”) evaluate to true?
- Script Logic: Does any embedded script within the ACL return true?
If the collective evaluation of ‘write’ ACLs grants permission, canWrite() returns true. Otherwise, it returns false.
Practical Example: Checking Write Access on Incident Records
Let’s again use the provided example and elaborate on its implications.
var inc = new GlideRecord ('incident');
gs.print (inc.canWrite ());Result: If the current user has roles (like ‘itil’ or ‘admin’) that allow them to modify incident records (i.e., table-level and potentially field-level write ACLs are met), the output will be true. If they don’t, it will be false.
Beyond the Basic: Nuances of `canWrite()`
Specific Record Context is Key for Write:
canWrite()is most meaningful when called on a specific record that has been queried or initialized. ACLs often have conditions based on the record’s current state or ownership.var myIncident = new GlideRecord('incident'); myIncident.addQuery('caller_id', gs.getUserID()); // Query incidents where I am the caller myIncident.query(); while (myIncident.next()) { gs.print('Incident ' + myIncident.number + ' - Can I write? ' + myIncident.canWrite()); if (myIncident.canWrite()) { // Only attempt to update if allowed myIncident.comments = 'Updated by script because I have write access.'; myIncident.update(); gs.print(' --> Successfully updated comments.'); } else { gs.print(' --> Cannot update comments due to ACLs.'); } }This script iterates through incidents where the current user is the caller. The
canWrite()check ensures that updates only occur on records the user is authorized to modify.New Records and `canWrite()`:
When you use
var gr = new GlideRecord('table'); gr.initialize();, callinggr.canWrite()at this point would typically check if the user has permission to *create* a new record (as writing to a newly initialized record is part of the creation process, often governed bycreateACLs which might then enable write ACLs for the new record). Aftergr.insert(),canWrite()would then check permissions for subsequent updates to that now-existing record.Field-Level Write ACLs:
Similar to `canRead()`, `canWrite()` on the `GlideRecord` object checks if you can write to the record generally. However, specific fields might be individually protected by field-level write ACLs. If `myIncident.canWrite()` is true, but a particular field (e.g., `priority`) has a field-level ACL preventing your user from writing to it, `myIncident.setValue(‘priority’, 1)` might still succeed but the change to `priority` won’t be saved or might trigger another ACL denial when `update()` is called. It’s often safer to rely on the platform’s enforcement during `update()` and check `canWrite()` as a general gate. For precise field-level UI control, client-side GlideAjax calls evaluating server-side ACLs or `g_form.isReadonly()` are more common.
Business Rules & Workflows:
When
canWrite()is called within a Business Rule or Workflow script, the security context is typically the system user or the user who triggered the event. Understanding this context is crucial for predicting the outcome of thecanWrite()check.
Troubleshooting `canWrite()` Issues
If users can’t save changes, or your script fails to update records, canWrite() is your primary suspect-identifier:
- Impersonate and Test: The absolute best way to troubleshoot is to impersonate the user experiencing the issue and run your
canWrite()check in a background script, or directly interact with the form to see ACL debugger output. - Identify Relevant ‘Write’ ACLs: Use the ACL Debugger (
System Security > Debugging > Debug Security) to see which ‘write’ ACLs are being applied and which are failing for the table/fields in question. - Role Verification: Double-check that the user possesses all the roles specified in the relevant ‘write’ ACLs.
- Condition Evaluation: Are the conditions on the ‘write’ ACLs met? For example, an ACL might only allow writing if the ‘State’ is ‘New’ and the record is ‘Assigned to me’.
- Scripted ACL Logic: If ‘write’ ACLs contain scripts, review them carefully for any logic that might be inadvertently denying access. Use
gs.log()statements within the ACL script for step-by-step debugging. - Other Restrictions: Sometimes, it’s not an ACL directly. Is the record locked by a workflow state (e.g., a record in a “Closed” state might be read-only by design)? Are there UI Policies or Client Scripts making fields read-only? (While these don’t directly affect
canWrite()on the server, they impact the user’s ability to modify).
Real-World Scenarios & Best Practices: Applying `canRead()` and `canWrite()`
Understanding these methods theoretically is one thing; applying them effectively in real-world ServiceNow development is where their true value shines. They transform your scripts from mere data manipulators into intelligent, security-aware agents.
The Why: Secure and User-Friendly Scripting
At its core, using canRead() and canWrite() is about building robust applications. It’s about:
- Preventing Errors: Rather than letting a script attempt an unauthorized operation and then crash or fail, you proactively check permissions.
- Maintaining Data Integrity: Ensuring that data is only viewed or modified by authorized personnel, adhering to strict security protocols.
- Improving Auditability: Scripts that respect ACLs lead to a more consistent and auditable security posture across the platform.
Enhancing User Experience (UX) – The Client-Side Connection
While canRead() and canWrite() are server-side methods, their results are often invaluable for client-side scripting to create a seamless user experience. Imagine a form where an “Edit” button only appears if the user has write access to the record, or specific fields are disabled if they are read-only.
To achieve this, you’d typically use GlideAjax to call a Script Include (which contains your GlideRecord.canWrite() logic) from a Client Script. The Script Include would return true or false, and your Client Script would then dynamically enable/disable UI elements accordingly.
// Example Client Script (e.g., onLoad)
function onLoad() {
var ga = new GlideAjax('MyACLUtils'); // Script Include name
ga.addParam('sysparm_name', 'canUserWrite'); // Function name in Script Include
ga.addParam('sysparm_table', g_form.getTableName());
ga.addParam('sysparm_sys_id', g_form.getSysId());
ga.getXMLAnswer(function(answer) {
if (answer === 'false') {
g_form.setReadOnly('short_description', true);
g_form.setReadOnly('description', true);
g_form.disableAttachments();
// Or hide/disable a 'Save' button
g_form.showFieldMsg('short_description', 'You do not have write access to this record.', 'info');
}
});
}
// Example Script Include: MyACLUtils (Client callable = true)
var MyACLUtils = Class.create();
MyACLUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
canUserWrite: function() {
var table = this.getParameter('sysparm_table');
var sysId = this.getParameter('sysparm_sys_id');
var gr = new GlideRecord(table);
if (sysId) {
gr.get(sysId);
} else {
gr.initialize(); // For new records, check create/write access
}
return gr.canWrite();
},
type: 'MyACLUtils'
});Preventing Data Integrity Issues
Always perform a canWrite() check before attempting to modify a record. This is a fundamental best practice, especially in Business Rules, Script Includes, or Fix Scripts that deal with mass updates or sensitive data. Imagine a scheduled job updating thousands of records – a quick canWrite() check can prevent an unauthorized mass modification that could take hours to revert.
// Example: Business Rule or Script Include
function processRecordForUpdate(grRecord) {
if (grRecord.canWrite()) {
grRecord.setValue('status', 'Processed');
grRecord.update();
gs.info('Record ' + grRecord.getUniqueValue() + ' updated successfully.');
} else {
gs.warn('Attempted to update record ' + grRecord.getUniqueValue() + ' but user lacks write access.');
}
}When to Use vs. When Not to Use (System Operations)
It’s important to differentiate between operations that *should* respect user ACLs and those that *must* bypass them (carefully!).
- Use When: Any script where the operation’s success is dependent on the *current user’s* permissions. This is the default and recommended approach for most custom applications and user-facing scripts.
- Do Not Use When (or use with caution): If you have a specific, validated business requirement to bypass security (e.g., a system-level process that updates records regardless of who triggered it, like an integration user with elevated permissions), you might explicitly grant roles to a system user or use techniques like
gs.setBypassMultipleScriptACLs(true);(and remember to set it back to false!). However, bypassing ACLs should be an exception, not the rule, and always documented with a strong justification.
Interviewer’s Delight: Common Questions and Scenarios
These methods are prime candidates for interview questions because they reveal a developer’s understanding of both scripting and security best practices.
- “Explain
canRead()andcanWrite().” Your answer should cover what they do, why they’re important (ACL evaluation, security), and where they are used. - “How do you ensure your script respects ServiceNow’s security policies?” This is where you shine by mentioning
canRead(),canWrite(), and avoiding direct ACL bypass unless absolutely necessary. - “A user reports they can see a record but can’t edit it. How would you debug this using GlideRecord methods?” You’d mention impersonating the user, running
canRead()andcanWrite()in a background script, and then escalating to the ACL Debugger. - “When would you use these methods over simply trying to update a record and catching an error?” Emphasize proactive security, better user experience (not showing options they can’t use), and cleaner code instead of relying on reactive error handling.
Final Thoughts: Empowering Your ServiceNow Development Journey
canRead() and canWrite() are far more than just methods; they are integral components of secure, robust, and user-friendly ServiceNow development. By understanding their nuances and integrating them into your daily scripting practices, you’re not just writing code; you’re building applications that respect the platform’s architecture, protect sensitive data, and provide a superior experience for your users.
So, the next time you’re about to read or modify a record in a server-side script, pause for a moment. Ask yourself: “Does this user (or system process) truly canRead() or canWrite() this data?” Your answers, guided by these powerful GlideRecord methods, will lead you to better, more secure, and more reliable ServiceNow solutions. Happy coding!