Skip to main content
Each script type has its own context and actions modules tailored to that automation scenario.

Overview

Request Review scripts run when an access request is assigned to a service user for review. The script can automatically approve, deny, or add comments to the request. This guide details the context and action modules you can use to get started, as well as the best practices and limitations when using OpalScript. For use case specific sample scripts, see our examples.

Quick Start

The simplest script approves all requests:
actions.approve()
A more practical script evaluates the request:
request = context.get_request()

if "urgent" in request.reason.lower():
    actions.approve("Auto-approved: urgent request")
else:
    actions.comment("Flagged for manual review")

context Module

The context module provides read-only access to the request being reviewed.

Get the request object

context.get_request() returns the Request object being reviewed.
Parameters
None
Returns
Request
object
Contains information about the access request
Example
request = context.get_request()
print(request.reason)
print(request.requester_id)

actions Module

The actions module provides methods to take action on the request.

Approve a request

actions.approve([comment]) approves the request.
Parameters
comment
string
Optional comment
Example
actions.approve()
actions.approve("Auto-approved: meets all criteria")

Deny a request

actions.deny(comment) denies a request. A comment is required to explain the denial.
Parameters
comment
string
required
Comment to add
Example
actions.comment("Flagged: unusual access pattern detected")

Comment on a request

actions.comment(comment) adds a comment without changing the request status. Useful for flagging requests or adding context for manual reviewers.
Parameters
comment
string
required
Comment to add
Example
actions.comment("Flagged: unusual access pattern detected")

Objects

Request object

Returned by context.get_request() and contains information about the access request.
AttributeTypeDescription
idString (UUID)Unique identifier for the request
reasonStringThe reason provided by the requester
requester_idString (UUID)ID of the user who created the request
target_user_idString or NoneID of the user being granted access (if applicable)
target_group_idString or NoneID of the group being granted access (if applicable)
requested_duration_minutesint or NoneRequested access duration in minutes
statusStringCurrent status (e.g., "PENDING", "APPROVED", "DENIED")
requested_resourcesList[RequestedResource]Resources included in this request
requested_groupsList[RequestedGroup]Groups included in this request
custom_fieldsDict[String, value]Custom field values from the request template
Example Usage
request = context.get_request()

# Basic properties
reason = request.reason
requester = request.requester_id

# Check optional properties
if request.requested_duration_minutes:
    duration = request.requested_duration_minutes

# Iterate over requested resources
for resource in request.requested_resources:
    print(resource.resource_name)

# Access custom fields
ticket_number = request.custom_fields.get("ticket_number", "")

RequestedResource object

Represents a resource included in the request. Returned in the Request object.
AttributeTypeDescription
idString (UUID)Unique identifier for this requested resource entry
resource_idString (UUID)ID of the resource being requested
resource_nameString or NoneName of the resource
resource_typeString or NoneType of the resource (e.g., "AWS_IAM_ROLE")
access_level_nameStringDisplay name of the requested access level
access_level_remote_idStringRemote identifier for the access level
Example Usage
for resource in request.requested_resources:
    print(f"Resource: {resource.resource_name}")
    print(f"Type: {resource.resource_type}")
    print(f"Access Level: {resource.access_level_name}")

RequestedGroup object

Represents a group included in the request. Returned in the Request object.
AttributeTypeDescription
idString (UUID)Unique identifier for this requested group entry
group_idString (UUID)ID of the group being requested
group_nameString or NoneName of the group
group_typeString or NoneType of the group (e.g., "OKTA_GROUP")
access_level_nameStringDisplay name of the requested access level
access_level_remote_idStringRemote identifier for the access level
Example Usage
for group in request.requested_groups:
    print(f"Group: {group.group_name}")
    print(f"Type: {group.group_type}")

Custom Fields

The custom_fields attribute is a dictionary containing values from the request template’s custom fields. The keys are field names, and values depend on the field type:
Field TypeValue TypeExample
Short TextString"JIRA-1234"
Long TextString"Detailed justification..."
BooleanboolTrue
Multi-ChoiceString"Option A"
Example
custom_fields = request.custom_fields

# Access with default value
ticket = custom_fields.get("ticket_number", "")
is_emergency = custom_fields.get("emergency_access", False)

# Check if field exists
if "justification" in custom_fields:
    justification = custom_fields["justification"]

Constraints & Limits

OpalScript enforces limits to ensure safe, predictable execution:
ConstraintLimitDescription
Script Size100 KBMaximum script length
Execution Time30 secondsScripts timeout after 30 seconds
Execution Steps1,000,000Maximum operations to prevent infinite loops

Unsupported Operations

For security and reliability, OpalScript does not support:
  • External HTTP calls: Scripts cannot make network requests
  • File I/O: Scripts cannot read or write files
  • Direct database access: All data access goes through provided modules
  • Import statements: All modules are pre-loaded
  • While loops: Use for loops with range() instead

Error Handling

Scripts can fail due to various errors. Understanding common error types helps you write more robust scripts.

Syntax Errors

# Missing colon
if True
    actions.approve()  # Error: missing colon

None Value Errors

request = context.get_request()

# Unsafe: duration might be None
if request.requested_duration_minutes < 60:  # Error if None
    actions.approve()

# Safe: check for None first
if request.requested_duration_minutes and request.requested_duration_minutes < 60:
    actions.approve()

Type Errors

# Error: concatenating string and int
message = "Count: " + 5

# Correct: convert to string
message = "Count: " + str(5)

Best Practices

  1. Check for None before using optional attributes
  2. Use .get() with defaults when accessing dictionary values
  3. Keep scripts focused - do one thing well
  4. Test with edge cases - empty strings, None values, missing fields
# Defensive coding example
request = context.get_request()

# Safe dictionary access
custom_fields = request.custom_fields
ticket = custom_fields.get("ticket_number", "")

# Safe optional attribute access
duration = request.requested_duration_minutes
if duration is not None and duration <= 240:
    actions.approve("Short duration approved")
elif duration is None:
    actions.comment("No duration specified")
else:
    actions.comment("Duration requires review")