> ## Documentation Index
> Fetch the complete documentation index at: https://doc.lucidworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Trimming

> Query pipeline stage configuration specifications

export const schema = {
  "type": "object",
  "title": "Security Trimming",
  "description": "Apply connectors security trimming",
  "properties": {
    "skip": {
      "type": "boolean",
      "title": "Skip This Stage",
      "description": "Set to true to skip this stage.",
      "default": false,
      "hints": ["advanced"]
    },
    "label": {
      "type": "string",
      "title": "Label",
      "description": "A unique label for this stage.",
      "hints": ["advanced"],
      "maxLength": 255
    },
    "condition": {
      "type": "string",
      "title": "Condition",
      "description": "Define a conditional script that must result in true or false. This can be used to determine if the stage should process or not.",
      "hints": ["code", "code/javascript", "advanced"]
    },
    "legacy": {
      "type": "boolean",
      "title": "Legacy",
      "description": "True if this stage only supports legacy mode",
      "hints": ["readonly", "hidden"]
    },
    "asyncConfig": {
      "type": "object",
      "title": "Asynchronous Execution Config",
      "required": ["enabled", "asyncId"],
      "properties": {
        "enabled": {
          "type": "boolean",
          "title": "Enable Async Execution",
          "description": "Run the expensive data loading or processing part of this stage in a separate thread allowing the pipeline to continue executing. The results of this asynchronous execution can be merged into the pipeline request using a downstream \"Merge Async Results\" stage.",
          "default": false
        },
        "asyncId": {
          "type": "string",
          "title": "Async ID",
          "description": "A unique value to use as reference in downstream \"Merge Async Results\" stages."
        }
      }
    },
    "overrideUserIdentityHandling": {
      "type": "boolean",
      "title": "Override Default User Identity Handling?",
      "description": "Default handling first attempts to take the user identity from a 'fusion-user-id' http-header, which is the logged-in user ID from the Fusion proxy service. If that value is empty, a 'username' query parameter is tried instead. When this DataSource property is enabled, the specified source and key properties are used explicitly, without any fallback behavior.",
      "default": false
    },
    "userIdentitySource": {
      "type": "string",
      "title": "User ID source",
      "description": "Specify whether the value comes from an http header or query parameter.",
      "enum": ["query_param", "header"],
      "default": "query_param"
    },
    "userIdentityKey": {
      "type": "string",
      "title": "User ID key",
      "description": "e.g. username, userID, etc.",
      "default": "username"
    },
    "datasources": {
      "type": "array",
      "title": "Restrict filter to Datasource(s)",
      "description": "A list of Fusion datasources to which security-trimming should be restricted, allowing content from other datasources to pass through un-filtered; if empty, all matching content is subject to filtering.",
      "hints": ["advanced"],
      "items": {
        "type": "string"
      }
    }
  },
  "category": "Set Up",
  "categoryPriority": 8,
  "unsafe": false
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const formatDescription = str => {
    const s = sanitize(str);
    return (/[.!?]\)*$/).test(s) ? s : `${s}.`;
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  return <div>
      {description && <p>{formatDescription(description)}</p>}

      {visibleProps.map(([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...prop.title && ({
        post: [<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>]
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
            {prop.description && <p>{formatDescription(prop.description)}</p>}

            {isComplexDefault && <div className="flex">
                <p>
                  <strong>Default:</strong>
                </p>
                <pre className="!my-0">
                  <code>
                    {JSON.stringify(rawDefault, null, 2)}
                  </code>
                </pre>
              </div>}

            {isArrayOfObjects && <div className="flex">
              <p>
                <strong>Object attributes:</strong>
              </p>
              <pre className="!my-0">
                <code>
                  {'{\n'}
                  {Object.entries(prop.items.properties).map(([iname, iprop]) => <>
                      {`  ${iname}`}
                      {prop.items?.required?.includes(iname) && <span style={{
      color: 'red'
    }}> required</span>}
                      {`: {\n    display name: ${sanitize(iprop.title || '')}\n    type: ${iprop.type}\n  }\n`}
                    </>)}
                  {'}'}
                </code>
              </pre>
              </div>}

            {isObject && <Expandable title="properties">
                <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
              </Expandable>}
          </ParamField>;
  })}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

[localhost link]: http://localhost:3000/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/security-trimming

[mintlify link]: https://doc.lucidworks.com/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/security-trimming

[old doc.lw link]: https://doc.lucidworks.com/managed-fusion/5.9/hnw5gy

<Note>
  **Important**

  This stage is deprecated in Lucidworks Search 5.9.0. The [Graph Security Trimming stage](/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/graph-security-trimming), introduced in Lucidworks Search 5.6.0, uses a single filter query for all data sources instead of one filter query per data source. Migrate your query pipeline stage to the graph security trimming stage.
</Note>

<Accordion title="Migrate older Fusion Graph Security Trimming stage setups to Lucidworks Search 5.9">
  This describes how to migrate your Lucidworks Search releases prior to 5.8 Graph Security Trimming query pipeline stage setup to Lucidworks Search 5.8 or later.

  It applies to deployments using:

  * SharePoint Optimized V2 connector v1.1.0 or later
  * LDAP ACLs V2 connector v1.4.0 or later to crawl Active Directory in Azure
  * The LDAP ACLs V2 connector v1.2.0 or later to crawl Active Directory in LDAP

  ## Migration

  To migrate a deployment that is crawling Active Directory to Lucidworks Search 5.8 or later, follow these steps.

  ### Update the datasource configurations

  The SharePoint Optimized V2 and LDAP ACLs V2 datasources must index the content documents and ACL documents to the same collection. Ensure both datasources use the same value, `contentCollection`, for the field **ACL Collection ID**.

  1. Sign in to Lucidworks Search and click your application.
  2. Click **Indexing > Datasources**.
  3. Open your SharePoint Optimized V2 or LDAP ACLs V2 datasource.
  4. Under Security, update the configuration to use `contentCollection` as the ACL Collection ID. The Security checkbox must be checked for this field to display.
  5. Save the configuration.

  Repeat this process for all required datasources.

  ### Clear and the datasources and perform a full crawl

  1. Click **Indexing > Datasources**.
  2. Open your SharePoint Optimized V2 or LDAP ACLs V2 datasource.
  3. Click **Clear Datasource > yes**.
  4. Click **Collections > Collections Manager**.
  5. Verify the `job_state` collection is empty.
  6. Return to your datasource.
  7. Click **Run > Start** to reindex your data.

  Repeat this process for all required datasources.
</Accordion>

The Security Trimming query pipeline stage restricts query results according to the user ID. While indexing the content, the Lucidworks Search connectors service stores security ACL metadata associated with the crawled items and indexes them as fields. The Security Trimming stage matches this information against the ID of the user running the search query.

This stage supports [asynchronous processing](/docs/lucidworks-search/05-move-data-out/query-pipeline/overview).

<LwTemplate />

## Configuration

<Tip>
  When entering configuration values in the UI, use *unescaped* characters, such as `\t` for the tab character. When entering configuration values in the API, use *escaped* characters, such as `\\t` for the tab character.
</Tip>

<SchemaParamFields schema={schema} />
