Custom Strategies

Define Access Strategies for services that Sym does not support out of the box.

Overview

In addition to native Access Strategies and AWS Lambda, Sym provides an interface that enables implementers to define fully custom Access Strategies.

πŸ“˜

Want to jump right in? Try the quickstart!

If you're ready to dive in, these resources have everything you need to start building:

Custom Strategy Framework

The Sym Strategy Framework provides the tools to:

  • Define custom Strategy logic for escalate, deescalate, and identity fetching/matching
  • Define user-facing targets to which the Strategy can be applied
  • Deploy the Strategy as a custom Flow.

Of course, as with any Sym Strategy, implementers can also define Workflow Handlers to customize, automate, and route their Flows.

πŸ“˜

Terraform is highly variable

The below examples assume a configuration in which multiple Sym Flows are defined as separate modules, and brought together through a single main.tf file. Your configurations may vary. For full context on the below, be sure to check out the whole repo from which these examples are pulled.

The Custom logic

The bulk of the Custom Strategy is defined in the strategy.py file that lives alongside your Flow definition and implementation.

🚧

Restrictions Apply

Your Custom Strategy will be executed inside of a RestrictedPython environment, and as a result certain methods might not be available (e.g. all(), next()).

In addition, only imports from the following modules are allowed:

  • sym.sdk
  • requests
  • json
  • datetime
from sym.sdk.strategies import AccessStrategy
from sym.sdk.integrations import slack


class CustomAccess(AccessStrategy):
    def fetch_remote_identity(self, user):
        return user.email

    def escalate(self, target_id, event):
        requester = self.get_requester_identity(event)
        target_identifier = event.payload.fields["target"].settings["identifier"]
        slack.send_message(slack.user(requester), f"Access to {target_identifier} granted!")

    def deescalate(self, target_id, event):
        requester = self.get_requester_identity(event)
        target_identifier = event.payload.fields["target"].settings["identifier"]
        slack.send_message(slack.user(requester), f"Access to {target_identifier} revoked!")

Declaring the Flow, Strategy, and Target definitions

Like any Sym Strategy, a Custom Strategy needs to be declared in Terraform.

πŸ“˜

The get_identity reducer is not used by Custom Strategies

While you can use the same impl.py file for all your flows, including flows that use Custom Strategies, Custom Strategies never call the get_identity reducer. Instead, implement your custom identity discovery logic in the fetch_remote_identity() method of your Custom Strategy.

# A custom Integration can be used to access secrets in your custom strategy implementation
# as well as manage identities.
resource "sym_integration" "custom" {
  type        = "custom"
  name        = local.flow_name
  external_id = var.integration_identifier

  settings = {
    secret_ids_json = jsonencode([sym_secret.api_key.id])
  }
}

# The targets that your Sym Strategy manages access to.
resource "sym_target" "targets" {
  for_each = { for target in var.targets : target["identifier"] => target["label"] }

  type = "custom"

  name  = "${local.flow_name}-${each.key}"
  label = each.value

  settings = {
    identifier = each.key
  }
}

# The Strategy your Flow uses to manage access.
resource "sym_strategy" "this" {
  type = "custom"

  name           = local.flow_name
  implementation = "${path.module}/strategy.py"
  integration_id = sym_integration.custom.id
  targets        = [for target in var.targets : sym_target.targets[target["identifier"]].id]
}

# The Flow that grants users access to custom targets.
resource "sym_flow" "this" {
  name  = local.flow_name
  label = "Custom Quickstart"

  template = "sym:template:approval:1.0.0"

  implementation = "${path.module}/impl.py"

  environment_id = var.sym_environment.id

  vars = var.flow_vars

  params = {
    strategy_id = sym_strategy.this.id

    prompt_fields_json = jsonencode(
      [
        {
          name     = "reason"
          type     = "string"
          required = true
        }
      ]
    )
  }
}

Deploying the Strategy

Finally, the full Strategy is deployed as a Flow.

module "custom_access_flow" {
  source = "../modules/custom-access-flow"

  flow_vars        = var.flow_vars
  secrets_settings = module.sym_runtime.secrets_settings
  sym_environment  = module.sym_runtime.prod_environment
  targets          = var.targets
}

Did this page help you?