Broadleaf Microservices

Admin Security

Authentication

See Auth Services documentation for overview of how authentication and authorization works.

Component User Restrictions

Restricting Access to Routes

The admin supports restricting access to certain routes depending on the user’s permissions. To do so supply a security scope to the component. This will require the user has the right permission to view or modify that component.

@Bean
@ConditionalOnMissingBean(name = "adminUserMetadataRoutes")
public ComponentRouteLocator adminUserMetadataRoutes(RoutesBuilder routesBuilder) {
    return routesBuilder.routes()
            .route("/users",
                    r -> r.componentId(AdminUserIds.BROWSE)
                            .scope(AdminUserScopes.ADMIN_USER)) // <--
            .route("/users/create",
                    r -> r.componentId(AdminUserIds.CREATE)
                            .scope(AdminUserScopes.ADMIN_USER)) // <--
            .route("/users/:id",
                    r -> r.componentId(AdminUserIds.UPDATE)
                            .scope(AdminUserScopes.ADMIN_USER)) // <--
            .build();
}

Field-level Restrictions

Note
As of 1.8.3-GA and 1.9.0-GA we have introduced field-level user-access restriction.

It is possible to restrict form fields based on a user’s permissions. To do so, supply a security scope in the component’s metadata:

Fields.string()
    .label("product.fields.sku")
    .scope(ProductScopes.PRODUCT) // <--

The field will only render if the user has ALL_<SECURITY_SCOPE> or READ_<SECURITY_SCOPE>. It will only be editable if the user has ALL_<SECURITY_SCOPE>, CREATE_<SECURITY_SCOPE>, or UPDATE_<SECURITY_SCOPE>.

This also applies to Groups, Externals, and Columns.

Content Security Policies

As of 1.8.3-GA and 1.9.0-GA, we have introduced Content Security Policy headers for the admin app.

With this there are several changes introduced:

  • All inline scripts have been moved to separate js files

    • The process of injecting runtime environment variables into index.html has been replaced with a shell script run when the docker image for adminweb is run. The variables starting with VITE_ and NODE_ENV will be written to public/env.js. This file is now imported into index.html as an external JS file.

      • This should be seamless change for clients.

    • The inline script in public/silent-callback.html is now imported from public/silent-callback.js.

  • The vite property build.assetsInlineLimit has been set to 0, disabling inlining of images. This was not a feature that was in use, so it should not be noticed.

  • CSP headers are now set in index.js.

Configuration

It is possible to disable the use of this header by setting CONTENT_SECURITY_POLICY_ENABLED to false. Alternatively, report-only mode may be enabled with CONTENT_SECURITY_POLICY_REPORT_ONLY.

Tip

When using report only mode, it will be useful to also set up an endpoint to receive these reports, otherwise they will only appear in the browser’s dev console. To set the endpoint use SECURITY_HEADERS_REPORT_TO, which will set the Report-To header; then set CONTENT_SECURITY_POLICY_REPORT_TO to set the report-to directive in the Content-Security-Policy header. See MDN Web Docs for more details on the format to use with CONTENT_SECURITY_POLICY_REPORT_TO.

Example:

Example Report-To header value
{
    "group": "csp-endpoint",
    "max_age": 86400,
    "endpoints":
    [
        {
            "url": "https://example.com/csp-reports"
        }
    ]
}
Example report-to directive in Content-Security-Policy header
Content-Security-Policy: ...; report-to csp-endpoint
The effective default CSP header is as follows:
default-src 'self';
script-src 'self';
connect-src 'self';
img-src 'self' https: data:;
style-src 'self' fonts.googleapis.com 'nonce-<uuid.v4>';
font-src 'self' fonts.gstatic.com;
base-uri 'self';
form-action 'self';
child-src 'self';
frame-src 'self';
frame-ancestors 'self';
Table 1. To control the particular values of these either modify them directly in samples/express/index.js or use the following properties.
Directive Property Default Value Notes

default-src

CONTENT_SECURITY_POLICY_DEFAULT

'self'

connect-src

CONTENT_SECURITY_POLICY_CONNECT

'self'

script-src

CONTENT_SECURITY_POLICY_SCRIPT

'self'

img-src

CONTENT_SECURITY_POLICY_IMG

'self' https: data:

It is recommended to restrict this further based on the actually expected image domains such as 'self' www.mycompany.com. The HTML field component uses some data type images.

style-src

CONTENT_SECURITY_POLICY_STYLE

'self' fonts.googleapis.com 'nonce-<uuid.v4>'

By default, some google fonts are loaded. To support inline styles used by some components, we generate a nonce (v4 UUID) by default in the sample ExpressJS server. This is added to a custom "csp-nonce" meta tag in index.html to make it available in the browser.

font-src

CONTENT_SECURITY_POLICY_FONT

'self' fonts.gstatic.com

base-uri

CONTENT_SECURITY_POLICY_BASE_URI

'self'

form-action

CONTENT_SECURITY_POLICY_FORM

'self'

child-src

CONTENT_SECURITY_POLICY_CHILD

'self'

frame-src

CONTENT_SECURITY_POLICY_CHILD

'self'

This is set using the same property as child-src for now for browser-compatibility. Previously, it was deprecated in CSP level 2, but in CSP level 3 it was undeprecated.

frame-ancestors

CONTENT_SECURITY_POLICY_FRAME_ANCESTORS

'self'

Important

The default value for script-src means that strings cannot be evaluated as JavaScript in the app. Practically, this means that any templates defined in metadata that require such evaluation like .previewPath("${url ? '/browse' + url : '/'}") must be rewritten to require only simple string replacement: /browse${url}. (In the particular case of previewPath defined for Products and Categories for preview-on-site functionality, there is no need to default to / as the component does so already.) The vast majority of metadata string templates should require no migration.

If necessary, to allow unsafe-eval temporarily, set CONTENT_SECURITY_POLICY_SCRIPT="'self' 'unsafe-eval'" and VITE_ENABLE_STRING_EVAL=true. To set either can be done in a docker-compose or .env file. CONTENT_SECURITY_POLICY_SCRIPT is used by the node server while VITE_ENABLE_STRING_EVAL is used by the client-side code.

Permissions and Feature Policy Headers Support

We also introduced support for setting the Feature-Policy header and its newer, partially adopted successor Permissions-Policy.

Note
Browser support for Permissions-Policy is quite limited currently.

Use FEATURE_POLICY_DIRECTIVES to set the Feature-Policy header and PERMISSIONS_POLICY_DIRECTIVES for Permission-Policy. By default, neither of these headers is used. Additionally, either of these may be set to report-only mode with FEATURE_POLICY_REPORT_ONLY or PERMISSIONS_POLICY_REPORT_ONLY.

Tip

When using report only mode, it will be useful to also set up an endpoint to receive these reports, otherwise they will only appear in the browser’s dev console. To set the endpoint use SECURITY_HEADERS_REPORT_TO, which will set the Report-To header.

  • See the w3c specification for more details on the format to use for the Permissions-Policy header.

    • Example:

      {
          "group": "default",
          "max_age": 86400,
          "endpoints":
          [
              {
                  "url": "https://reportingapi.tools/public/submit"
              }
          ]
      }
  • See the w3c docs for more details on the format to use Feature-Policy.

    • Example:

      {
          "group": "default",
          "max_age": 86400,
          "endpoints":
          [
              {
                  "url": "https://reportingapi.tools/public/submit"
              }
          ]
      }