ACL

TALY comes with an ACL (Access Control List) implementation for the frontend. It's loosely based on the XACML Standard. ACL is one of the main pillars of TALY as it enables you to write flexible Building Blocks that can be heavily adjusted in generated journeys.

1. Core Concepts

The idea of ACL is to enable developers to write very flexible Building Blocks that can be adjusted to a wide variety of applications' needs. It allows to control the visibility and editability of parts in frontend applications. The following subsections briefly explain the core concepts of ACL.

1.1. Resource

Everything is a "Resource". Resources are the things that make ACL work. You can think of resources as all the little things (mostly the visual ones) that make up a component (and thus a Building Block). A resource could be the headline of a Building Block or the first-name input of a form or anything else. Each resource is identified by a unique hierarchical path. Due to this hierarchical approach, two Building Blocks might very well both contain a resource with the same name (e.g. headline). When used in an application, these resources would be identified by their full ACL paths like page-a/header-building-block/headline or page-c/offerings/headline.

1.2. Condition

A Condition allows to restrict an ACL rule to be active only in certain situations, depending for example on the state of the app.

Conditions are evaluated using the library filtrex. Please refer to their documentation for details on how to write conditions. In addition to what filtrex provides out of the box, you can use the function s() to use values from the application state in your conditions.

1.3. State

By default every resource is visible and editable. Depending on the ACL configuration, a resource can be in (m)any of these states:

  • readonly
  • disabled
  • hidden

Take a look at the Example Policies at the bottom of this page for details.

1.4. Rule

The configuration for all resources is made up of "Rules". Each rule consists of a target resource, a condition and a desired state for that resource. A rule is written in plain text as a comma-separated-value in the following format:

<resource>, <condition>, <state>

Real life examples could look as simple as this (note the empty condition):

*/headline, , hidden

or could get as complex as this:

*/injured-driver-details/questions/*,  s("$['expert-driver-selection'].selectedDriver") == "ANOTHER_DRIVER" and s("$['driver-details'].driver.party.partyType") == "INCOMPLETE_PERSON", hidden

1.5. Policy

The entirety of the rules that describe how resources need to behave is called the "Policy". Each application is expected to provide this in a file called policy.txt. The sequence of the rules is important. It follows a top-to-bottom precedence. The rules that are placed above can overrule the below lines.

1.6. Engine

The ACL "engine" drives the ACL system. This engine receives an app's Policy and takes care of validating the rules' conditions to dynamically adjust the applied rules at run-time.

2. Installation and Setup

The ACL package is not tied to any other package in the library, making it easy to use in any Angular project.

2.1. ACL Package

ACL can be found in the @allianz/taly-acl package, which contains an engine for parsing and evaluating policies against incoming requests to grant or deny access. The @allianz/taly-acl/angular entry point provides all the necessary resources for implementing ACL in any Angular project.

2.2. Set Up ACL for Generated Applications

If you are generating a TALY journey inside a worskpace, installing the package is the only step required to set up ACL. For journey generation outside of workspaces, no setup is needed.

2.3. Set Up ACL for Non-generated Applications

In a non-generated application, you have to manually integrate the ACL-related modules, services, and providers into the AppModule.

🧩 An example setup can be found here.

import {
  ACL_POLICY_CONTENT_TOKEN,
  AclInspectorService,
  AclModule,
  AclService
} from '@allianz/taly-acl/angular';
import { InputElementInjectorModule } from '@allianz/taly-acl/input-element-injector-directive';

@NgModule({
  imports: [AclModule, InputElementInjectorModule],
  providers: [
    {
      provide: ACL_POLICY_CONTENT_TOKEN,
      useValue: POLICY_CONTENT
    },
    AclService,
    AclInspectorService
  ]
})
export class AppModule {}

Optionally, you can write ACL conditions based on the application state. In order to do so, you can provide an implementation for an adapter via the ACL_STORE_ADAPTER_TOKEN token.

For PFE applications, you can use the PfeAclExpressionAdapter from the @allianz/taly-pfe-connector package. In other cases, you can provide your own adapter implementation.

import { ACL_STORE_ADAPTER_TOKEN } from '@allianz/taly-acl/angular';
import { PfeAclExpressionAdapter } from '@allianz/taly-pfe-connector';

@NgModule({
  providers: [
    {
      provide: ACL_STORE_ADAPTER_TOKEN,
      useClass: PfeAclExpressionAdapter
    }
  ]
})
export class AppModule {}

3. Usage in Building Blocks

ACL was created as a means of ensuring that Building Blocks are configurable, which is particularly important when sharing these components with others. If a Building Block is only used within your own application and ACL is not necessary, then it can be omitted. However, if a Building Block is published in a library, such as those included in the ITMP documentation, then it's important to include ACL functionality so that others can configure the Building Block in their journeys.

To facilitate the consumption of ACL in Building Blocks, it is necessary to add a <building-block-name>.acl.yml file to your Building Block folder (more info below). This file should list all the ACL resources defined in the Building Block.

3.1. The *aclTag Structural Directive

Elements in the Building Block's template can be marked as a resource by adding the *aclTag="'resource-name'" structural directive to these elements. Such elements can then be hidden or shown using the hidden or visible state. In addition, form element resources can be disabled or marked as readonly. Take a look at the Forms section for details.

The *aclTag directive is provided in the TalyCoreModule that can be imported from @allianz/taly-core and is meant to be imported in your Building Block's module. The *aclTag directive accepts a string as value and can be used to mark an element in a component's template as an ACL resource. Elements with this directive can be targeted using ACL rules.

Example:

<h1 *aclTag="'headline'">This is my headline</h1>

3.2. Forms

ACL only supports Reactive Forms. By using the autoFormBindingFactory method from @allianz/taly-acl/form-support, you can conveniently enable ACL support for your form controls:

// Define `bindAclWithForm` before your Building Block component class
const bindAclWithForm = autoFormBindingFactory();

// Inside your Building Block component class
form = new FormGroup({
  nested: new FormGroup({
    myControl: new FormControl('')
  })
});

this.aclFormBinding = autoFormBindingFactory()(this.acl, this.form);
this.aclFormBinding.stream$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe();

💡 Everything in this snippet is automatically generated when you use the Building Block generator.

Apart from that, a nested form control path like nested.myControl will be expected by TALY to correspond to an ACL resource path nested/my-control. There are 2 crucial points to note here:

  • The ACL hierarchy you build via the ACL tags you define in your template needs to match the form structure defined in your component.
  • The ACL tags need to match the dasherized version of form control and form group names, as in myControl -> my-control.

This is what a template including *aclTag directives for the above form could look like to support the automatic form binding:

<div [formGroup]="formGroup">
  <div *aclTag="'nested'" [formGroup]="nested">
    <input *aclTag="'my-control'" formControlName="myControl" />
  </div>
</div>

3.3. Advanced Forms

In some cases, not all form elements need to be bound to ACL, so we have included the option to exclude certain ACL paths from the binding process. This is particularly useful when using FormArray with direct FormControls as children, as only FormGroups are supported, and console warnings may appear.

const formGroup = new FormGroup({
  control1: new FormControl(),
  group1: new FormGroup({
    nestedControl1: new FormControl(),
    nestedControl2: new FormControl()
  }),
  contactNumbers: new FormArray([new FormControl(), new FormControl()])
});

const bindAclWithForm = autoFormBindingFactory({
  excludes: ['contactNumbers.0', 'contactNumbers.1']
});
bindAclWithForm(formGroup);

3.4. Transient ACL tags

To enable ACL control over a form control or nested forms, a corresponding ACL tag must be added to the HTML template, following a similar structure to the DOM hierarchy. However, this structure prevents adding arbitrary ACL-enabled templates in between without affecting the ACL hierarchy. To address this limitation, transient ACL tags can be used. Transient ACL tags are invisible to nested ACL keys but still inherit parent ACL tags. They can be defined by setting the transient boolean input to true in the *aclTag directive.

⚠️ It's important to use this advanced functionality sparingly to avoid creating an ACL hierarchy that doesn't follow the natural DOM structure, making it more difficult to maintain. Additionally, developers should ensure that ACL hierarchies are created correctly to avoid potential bugs in running applications.

Example:

<h1 *aclTag="'title'"></h1>
<form [formGroup]="formGroup">
  <div formGroupName="person" *aclTag="'person'">
    <ng-container *aclTag="'person-group'; transient: true;">
      <input
        type="text"
        formControlName="firstName"
        placeholder="First name"
        *aclTag="'first-name'"
      />
      <input type="text" formControlName="lastName" placeholder="Last name" *aclTag="'last-name'" />
      <input type="file" formControlName="avatarImage" *aclTag="'avatar-image'" />
    </ng-container>
  </div>
</form>

With person-group set as transient, the form structure is still respected in the ACL structure. person-group does not become part of the ACL paths corresponding to the form controls, so any ACL rule applied to person/person-group won't have any effect on the form controls:

// ACL hierarchy
title
person/person-group // transient: true
person/first-name
person/name-name
person/avatar-image

3.5. The acl.yml file

All ACL resources of a Building Block need to be specified in a file called <bb-name>.acl.yml that is stored and published alongside the Building Block sources. This file is needed to allow app generation through the TALY generators and executors which need to be aware of the available ACL resources in any given Building Block.

For each ACL resource there needs to be an entry in the corresponding *.acl.yml file. Each entry needs to state

  • the relative path to that resource (simply the name for non-nested resources) as aclKey
  • the type of ACL resource. This could be either display for resources that can be shown/hidden but are not editable or form-control for resources that can also be enabled/disabled and/or marked as readonly
  • a label for that resource as label
  • optionally a description of the resource as description
  • optionally a default state for that resource as default with possible values of readonly, disabled, or hidden

label and description can be used in a Building Block library documentation to explain what this resource is about. The default can be used to state the default state of a resource. This allows to create opt-in resources that need to be shown/enabled explicitly.

Example

Here is an example of such a file for a building block with the resources title, title/icon, message, and input:

- aclKey: title
  aclType: display
  label: Title
  description: The title of this building block
- aclKey: title/icon
  aclType: display
  label: Title icon
  description: The icon shown next to the title (hidden by default)
  default: hidden
- aclKey: message
  aclType: display
  label: Message
  description: The message of this building block
- aclKey: input
  aclType: form-control
  label: Input
  description: The input of this building block

So far this file needs to be maintained manually so as a Building Block developer it is your responsibility to make sure that this file is in sync with the Building Block's ACL resources.

4. The ACL Inspector

The ACL inspector is a dashboard that can be part of a generated TALY journey during development and for debugging purposes. To add the ACL inspector to the generated journey, a specific option needs to be provided in the journey generation setup (see here and here).

The ACL inspector includes a fixed button in the upper right corner, which expands into a floating window that can be dragged and resized. This window provides a set of tabs with different functionalities, which are explained in the following subsections.

4.1. Policy Editor

The Policy Editor allows developers to verify and debug policies by disabling individual rules, changing resource or state values, and editing conditions.

The editor enables CRUD operations on all the rules from your policy file, as well as the ability to toggle those rules, manually refresh the policy, and copy updated policies for storage.

In addition, some Building Blocks define "default" ACL rules. These appear in the editor at the very end of the list of rules. They are read-only and can't be moved. If needed, they can be overridden by other rules, placed above them. Take a look at the acl.yml file section for details on where the "default" ACL rules are coming from and why they exist.

In the case of TALY-generated journeys, if the environment variable PERSIST_ACL_POLICY_URL is provided to the generation command, the policy editor will show a button to persist the policy. Clicking that button will trigger a POST request against the provided URL. The request body will contain the current policy in the officially supported format, ready to be used to generate the journey.

4.2. Reporter

The reporter tab lists all Decisions, showing what ACL resources are currently present or have been evaluated in the past. It holds knowledge about all seen ACL resources, making it a useful place to fetch information for building *.acl.yml files. However, it is important to ensure the accuracy of the list when copying it into the *.acl.yml file, as incorrect ACL resources may cause the generation of incorrect policies.

4.3. ACL Hints

You can optionally show a helping toolbar ("ACL Hint") for every ACL resource on a page:

There are two ways to enable these hints:

  • Use the checkbox "Show ACL Hints" in the Settings tab of the ACL Inspector
  • Toggle the ACl Hints by pressing the h key anywhere on the page (this shortcut is disabled if an input element is focused):

One can then conveniently click on the icons displayed on top of the resources to show or hide the corresponding resource.

5. Example Policies

Above we mentioned this regarding ACL rules in a policy:

The sequence of the rules is important. It follows a top-to-bottom precedence. The rules that are placed above can overrule the below lines.

To clarify this, here are some example policies and how they would be evaluated:

5.1. Resource States: Hidden, Readonly, and Disabled

# policy.txt

my-resource, , hidden
my-other-resource, , readonly
yet-another-resource, , disabled

Result

  • my-resource is always hidden
  • my-other-resource is always readonly
  • yet-another-resource is always disabled

5.2. Reset States: Visible and Editable

# policy.txt

my-resource, , visible
my-resource, , hidden

my-other-resource, , editable
my-other-resource, , disabled

yet-another-resource, , editable
yet-another-resource, , readonly

Result

  • my-resource is always visible. The visible rule appears before the hidden rule and therefore overrules it.
  • my-other-resource is always editable (i.e. not disabled). The disabled rule is overruled by the editable rule above it.
  • yet-another-resource is always editable (i.e. not readonly). The readonly rule is overruled by the editable rule above it.

5.3. Combination: Readonly and Disabled

# policy.txt

my-resource, , readonly
my-resource, , disabled

Result

my-resource is both readonly and disabled. These states are not mutually exclusive. Visually and logically, the disabled state wins and the readonly state has no effect. To overrule the disabled state, you have to add an editable rule between the readonly rule and the disabled rule.

💡 This behavior exists mostly for historical reasons. Changing this would be a subtle but potentially breaking change for a lot of journeys. If you think this behavior is confusing and should be changed, please reach out to us.

5.4. Combination: Hidden and Readonly/Disabled

# policy.txt

my-resource, , readonly
my-resource, , disabled
my-resource, , hidden

Result

my-resource is always hidden and readonly and disabled. Visually and logically, a hidden resource can't be disabled or readonly, so the readonly and disabled don't have any effect in this case.

5.5. Conditions

# policy.txt

my-resource, s("$.fullName") == "John Doe", editable
my-resource, "Laura" in s("$.names"), disabled
my-resource, , readonly

Result

  • my-resource is readonly if no other rule applies
  • my-resource is disabled when "Laura" is in the list of names at $.names
  • my-resource is editable (neither readonly nor disabled) if the name at $.fullName equals "John Doe"

5.6. Building Block State and ACL

When a Building Block is hidden through ACL, the state of that Building Block is removed from the PFE state. However, the corresponding data will be preserved and will be restored when the Building Block becomes visible again. Any new state data that was added while the Building Block was hidden will take precedence over the restored back-up.

results matching ""

    No results matching ""