1. Introduction

ConfigSeeder® is configuration management tool, not only capable of managing configuration values at a central place, but also manage their validity based on environment, application version, dates or labels.

ConfigSeeder® consists of different components:

Table 1. ConfigSeeder® Components
Name Purpose

ConfigSeeder® Management

Core component responsible to manage all configurations and provide configuration value retrieve services

ConfigSeeder® Value Provider

High availability configuration value replication

ConfigSeeder® Client

Clients for different languages and technologies.

  • Java: Core, Spring, Eclipse MicroProfile Config

  • Go

ConfigSeeder® Kubernetes Connector

Component distributing API keys, secrets and config maps from ConfigSeeder (under development)

ConfigSeeder® OS Connector

Component distributing files and configurations to different hosts. Can run under linux or windows and can optionally run as a service. (under development)

2. Quickstart

2.1. Start ConfigSeeder Management

  1. Download ConfigSeeder H2 Instance from https://configseeder.com/download/#cs-management

  2. Start instance with java -jar configseeder-server-management-app-h2-2.25.4.jar, or if you want to have persistence with H2, pass the database folder as follow: java -jar configseeder-server-management-app-h2-2.25.4.jar --spring.datasource.url=jdbc:h2:file:~/config-server.db. (For further options and configuration consult chapter ConfigSeeder Management Configuration)

  3. Open your Browser on http://localhost:8080/

  4. For providing security, a OIDC capable identity server is required. By default the delivered H2 instance is connected to a Keycloak Instance from Oneo GmbH.

    Login with user tenantadmin and password tenantadmin

  5. Create your first environments by clicking on the Environment link in the navigation bar. Then click on Create and fill in the form your new environment. (e.g. DEV, TEST and PROD). The most relevant value is the Key field, which will be used in future configurations.

    It’s a good practice to have no security restriction on any environment beside production. Leave everything on DERIVED for now.

    Create an environment
  6. As next create a configuration group. Navigate to Configuration Groups in the navigation bar and as next click on Create. In the form fill in the fields, where the most important part is again the Key field. We encourage to select either version or date for manage different configurations and reduce complexity. In this example, Date is disabled.

    Create a configuration
  7. Now you are ready to add new configurations. Click on your newly created Configuration Group and add your configuration. Depending on the current license different value types are supported. Create a new configuration and fill in the fields as desired. If different values for the same configuration key need to be created, click on the duplicate icon.

  8. In order to fetch values for your applications, you first need to create an API Key, which will give an application access to your configurations. Navigate to API Key and create a new one by clicking on Create. Define to which Configuration Groups and Environments the new key should grant access to. We also suggest to enter a period of validity as short as possible but no longer than two years.

    Create API Key - Step 1

    After clicking on Save the generated API Key is shown. You should keep it at a save place.

    Create API Key - Step 2

2.2. Start ConfigSeeder Clients

Time to connect your application to ConfigSeeder. For most of the following clients the general Client Introduction should be read first. Following options to integrate ConfigSeeder exists:

  1. Directly connect your application to ConfigSeeder with native clients

  2. Indirectly using a ConfigSeeder Connector.

    1. Curl

    2. Kubernetes Connector

    3. OS Connector

ConfigSeederOecoSystem

3. Applications

3.1. ConfigSeeder Management

3.1.1. Requirements

  • Environment

    • Database (PostgreSQL >= 9.5, SQL Server >= 2012)

    • Identity Provider

      • OIDC or OAuth2 compliant identity server (e.g. Keycloak, Okta)

      • SAML 2 complient identity server (IDP initiated SSO, SP initiated SSP)

      • Pr-Aauthenticated based on http headers

  • System

    • minimal: 512MB; recommended: 2GB

    • minimal: 1 core; recommended: >= 2 cores

    • JRE 17 (We recommend Zulu)

  • Browser

    • Chrome >= 56.X.X, Firefox >= 52 and other modern browsers

3.1.2. Setup steps

3.1.2.1. Preparations

Prepare before configuring ConfigSeeder Management:

  1. Decide which database you want to use (Supported: PostgreSQL, Microsoft SQL Server, Oracle)

  2. Get your trial or full ConfigSeeder license (mail at license@configseeder.com, mention: company name, database, number of configuration groups, number of environments)

  3. Setup Database (connection url, username, password) (empty database, user with DDL privilege)

  4. Identity Provider (urls, client id and secret, available profiles and attributes). Make sure groups or roles are available either as part of the access token or user info endpoint attribute)

  5. Define your hostname on which the application should be available

  6. If desired prepare a SSL Certificate

  7. Download ConfigServer Management instance from https://configseeder.com/download/#cs-management (Java JARs or Docker Image). Please be aware that the selection of your database has to match your licence.

3.1.2.2. Configure

Configurations can be passed in three variants and even mixed. We suggest to split up configurations as follow:

  • Non sensitive values: in a file called application.yaml

  • Sensitive values as environment variables

Follow these steps for the installation:

  1. Create application.yaml file

  2. Prepare configurations for:

    1. Database Configuration

    2. SSL Configuration

    3. Encryption Key Configuration

    4. Setup Authentication Mechanism

    5. Enable Profile: spring.profiles.active=setup

  3. Run ConfigSeeder Management

  4. Stop ConfigSeeder Management

  5. Cleanup configurations

    1. Remove setup profile spring.profiles.active=setup

    2. Remove sensitive values from the configuration and pass it as environment variables

  6. Start ConfigSeeder and save all configurations and scripts

3.1.2.3. Database

Independent of the configuration database you are using, the configuration options are the same:

Table 2. Database Configuration
Key M. Description

spring.datasource.url

M

JDBC URL to your database.

  • H2: jdbc:h2:file:~/<filename>;AUTO_RECONNECT=TRUE

  • PostgreSQL: jdbc:postgresql://<host>:5432/<database>

  • SQL Server:

    • unsecure: jdbc:sqlserver://<host>;database=<database>;encrypt=false;trustServerCertificate=true

    • secure: jdbc:sqlserver://<host>;database=<database>;integratedSecurity=true;encrypt=true;trustServerCertificate=false;trustStore=/path/to/truststore;trustStorePassword=changeit

spring.datasource.username

M

Username

spring.datasource.password

M

Password

spring.liquibase.change-log

Liquibase is used to migrate the database. By default nothing needs to be configured and the default is fine. But for older installations, another change-log needs to be selected:

  • For installations started before and with 2.23.x: classpath:db/db-migrations-liquibase.xml

  • Special case: SQL Server installations blocked by the upgrade due to Flyway license restrictions (updates from 2.20.6, 2.21.2): classpath:db/classpath:db/db-migrations-liquibase-21.xml

3.1.2.4. SSL Certificates

By default the application runs on port 8080 without encryption. You can easily force SSL connection by providing a SSL certificate. Follow SSL Certificate if you don’t know how to create a certificate.

Table 3. SSL Configuration
Key M. Description

server.port

Port on which ConfigSeeder should be running. For SSL we suggest port 8443 or 443

security.require-ssl

Set to true if you want to enforce SSL

server.ssl.key-store-type

Your keystore format. Our example uses PKCS12

server.ssl.key-store

Path to your keystore. E.g. /etc/configseeder/keystore.p12

server.ssl.key-store-password

Password to your keystore. In our example: changeit

server.ssl.key-alias

If more than one certificate is defined, use the alias or number. In our example: configseeder

3.1.2.5. Encryption Keys

ConfigSeeder Management has three encryption keys which needs to be configured. We strongly recommend setting your own values.

Table 4. Encryption Configuration
Key M. Description

configseeder.server.security.configurationvalue.encryption.secret

M

Encryption key to be used to encrypt the configuration values. Generate using AesKeyGenerator

3.1.2.6. Signing Key for JWT

The used encryption key for JWT will be used to sign all JWT. Changing the JWT signing key will result in invalid JWT.

You can either provide the JWT key inside a keystore or inline the PEM file location or event the content directly in the configuration. The first example shows how to configure the JWT using PKCS8 exported private and public key.

Table 5. JWT Configuration configured using PKCS8 PEM files
Key M. Description

configseeder.server.security.auth.api-key.credential.private-key-location

M

Private key als PKCS8 PEM file. Either the location needs to specified (e.g. /keys/jwt.pkcs8) or the content can be inlined using the data:-prefix before the value.

configseeder.server.security.auth.api-key.credential.public-key-location

M

Public key als PKCS8 PEM file. Either the location needs to specified (e.g. /keys/jwt.pem) or the content can be inlined using the data:-prefix before the value.

configseeder.server.security.auth.api-key.allow-weak-key

O

Allows keys with less bits than 2048. Defaults to false.

Example of an inlined JWT private key:

configseeder:
  server:
    security:
      auth:
        api-key:
          credential:
            private-key-location: |
              data:-----BEGIN PRIVATE KEY-----
              MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDUMnDydy2+seU/e53IYctk0EmpAnSPAKJfc9yDrokrnGtW3FJzPjh31wTVJ2dWLhqAAKuFH65DKOHXcE/vSRc5JAi0dYfWvuTuQfG1INs5XP8k/OVV8T3qb31WDCaJhGMMO1+pagNj6FXVvWAoVnfYAnYjV8Am0yu0x6sHjBo9bP31ib4v6SAoXCx4hI0ShMxM2+5WVt/lIotLdQZGeKmcRtFmLxJqPiN7LAWNloIaUWxEyISsDD3O74LkZTwfAqyrfWWgW6zrs8g56fGZyVu23Z3Fyt2PBiX4A+Ba40bKvPzTrliR07ID1ywAvtvtao3jx67Q77UZbemNHc4KPYohO9jkWDCgxuOoU+CWmrYZViw2PAbaVDz1XT05amTN9aOiJdDLwz4YTHwFEUwnRItU3vRxJrmeIlQgFZnBYZvoM4nJDhrKmjoQIZa+o3VkHxNw2JEgzH0O1n5xI9a25X8FcZQhggyAzPFLFPwiEB450wi0i+vezIwJI1CWfwNBcasnYKJ33IqK1DOGiZQRJNDRKFmAT1yEYEoqmdECM43TrtfduH3jnd6RUTZYbYsezLimrGsMKEPsshNjkDxbcAu3woE9q5qzL6nBElL76HULwiynAw5zg/uegjxW+o5YAXDDROg3L5GPvjiaDR02hQzOJDLlPNUEElDHUcYKf5HqMwIDAQABAoICAEigpxXmAqs1OjC7P6rZBgOQYtXM+FYFso2XG0+aymo51Gdba4D0N+GUn5vRK8ZMa1eKYypiSIPGF15km4db7e3pzmztmTvABJ0jxvXmtAHzOYTQZjI/KqD13eE4sWv5+uix3bEfjt+AIvjQI3xK9CPj28mG/EaNmnT6rigcJNMHXfjPV2AQm6gUvEfXnm1LNJ3N3MqCW4mShDikaixumibZ+kvOKrBgakZ7L79hV1Bos/Tq6LX2TqVoRtWjbKt0f+MGPWVgW1QlKY/g6YdoatKxEZIkOCb+xLoZoY2aIp27h5FwJ/JpkZFM8UukB6RAByaCLuxmTwbQHtLk86N/oqnTHvPcW0VftNKdfoxmdMgwk2RGn+tQszVtWfaRr+SMyAZsoP5OontJ+y6KZGRCxvKq08jyYjEobYPMSsFyCSRbkZ0phQafMg7M/r4VXuecpuqbglPYwlmj7TaSm1J9x90XXvs1ZEFcU5hMW7JLrKVBK+1n6OOXO5y2O8odh1vt71VY6qyo6GJUHK5KABBibdC9Mjhxp4xS6i1rmZO+7/jK4Fokw3pixNNS1/qo8z+jyZHckIXW02sdn7jxSFxIfBE655vEanxkiVpOxPAetMKJjRMwPxdjeEn9549R/qQaxdcjkWdChRSdvoIC1okFQ+G5CpZqV6/pP/rXWSwuTMTRAoIBAQD/D39SM/FflLlhiW0MWXzyGKXmGJ4uU8ox0jMKORko1cPG5Kwlhpchf25Sj17gCGMU8PH3qLLFDNqJ3l6yQSrsxmKiLnRwM6jVGndPC/h+ILVaeetw+AiGBc4WcGV9ZylhTJDCpKTMWB9OyzsoE7nscRT8lZZ4JeBrO67Q/zntJtmVADGtW4xky7SS+C5u8B0BFx6QY4xb2GNbP1sBwsmjx9dnJ2WsOYiOGMTqJkLQSBJpWTVmC/+I7SdW1spL06Qwa5R0Kyfg06btF4wHpr/CLupsIydKQbSG2URHQNF1b/IfJ8cv0EUx17aBhYMToMAgz/AGB4cPcsqRx/11ULq5AoIBAQDU+obewoRvSJtmyH4OZk3sizSlLb9sFP4Dpsh6DLTZcWZkaDoNZKvnSsiCd6MAn97tJgAOFOKGGB92rjTsEE/pPGPXPEI2TCK4AqyyXKZUZYq+ZBedZFV7SyyUD2uEk1Am1JM4UZt3xWNsZTXvh0mobMATp0X3Qc9b2WrpYSzbXXOaZh9LWYyn0ZyuQmHX2e8ck9DrVYbU4bYDfxeMI8+WtSrWWL//je8HttsA8m2XAxPa9TvfNDfJ1OmqA7rs6KKnJ4mC9mwsc9nvjhndNlJRsDCYw8p3nCfWRxvzKGMjxVt/+ZVO6+2Dbe+67TIXyYl0Z1ulANfKBXaLGvzZ8eZLAoIBAB+PHlTggnQuJcXwyCJoDDK0ZAqVbzUSPwltFc2lBPztfjzk3HwELD0D1HW4saeMwh28NmRLZltFXOEEN7lcoOANJ/UjRFsBuVks3ihUtdl9WJMqVLx0pDFZFIfoPlaG5jLY02ySDKYQHUeVk+tAjZe44jzikWyqWxtJljbb7aHvVaq/DGaD1lo+bkhlFvG5Gg2he7Gn750JqDmJHEyaenKeboOf90+ZCtFyNSM3urqkwEpytE29HFAn798p0nwRYtZu6T56fIOwDI53hGcJ9d4kCfN5YgopGRo6Vd1vIruiL1Y1iMrcvUMOitF54J8UG9GziOJmGgsKTBA/q5fRdlkCggEAZ67jM9UcphSB2wLmI1p4uxx176ga1aJOmzZ5yLkg3e7P+zemc1i8O2hzvthBWY969paWVkSilsdtodhjWT0Dujc1H9ZtIYPlLxHWQyrJqrKYXeHagTwURWaueBIdtLgGwDCizzsjZaaJpX6MSGTjXgQ7ahX4AMxgB4YL/zvYm56eJd9rgsJAsH5iOY+FlJC7UH1Bqx/C+SY89uzLaOUSLXtbNGiWZ/IQGQBTE93xrh/IbcMPd7UzJ6Xob2VLfgQvExMzZbdTqDxqL65NNx037B7LOcOvcJVr62xI1hKmWQa+acn2fypqQZ6mo6jPxzbNHQcoXWbQpsAMGuJzkCKsiwKCAQA6xEZ2K2pCC/YnJz6aZZWK2LuoR7y+AIJIuJ++nmh0ohqqCeBHGzJb31UQ9mIbirO+P1zyKHV81KEZhpn7BTLGaClXKjar8NTkcZaL+VgbUEZIOdisfcCI68dng3AtZtd5MImq02M5kZLPevYaoVRiYIQJxTGA9XD0OTatlW8q+UgB5KeEVdgrqzO+I/hnpFaQpUptc8kAcFDkPeSbrFLhm/EbTJAi4tCaNXW+/aCr727tmCyuqLFPSttuGGKlI/PD55kBJd1n8mOxg8XIVVB0qSRsbMon3L55rlW6rLHPaOYr904kIQgIH1OfX7i+svv2Us4A0V/0HM9AL3fLEOCH
              -----END PRIVATE KEY-----
            public-key-location: |
              data:-----BEGIN PUBLIC KEY-----
              MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1DJw8nctvrHlP3udyGHLZNBJqQJ0jwCiX3Pcg66JK5xrVtxScz44d9cE1SdnVi4agACrhR+uQyjh13BP70kXOSQItHWH1r7k7kHxtSDbOVz/JPzlVfE96m99VgwmiYRjDDtfqWoDY+hV1b1gKFZ32AJ2I1fAJtMrtMerB4waPWz99Ym+L+kgKFwseISNEoTMTNvuVlbf5SKLS3UGRnipnEbRZi8Saj4jeywFjZaCGlFsRMiErAw9zu+C5GU8HwKsq31loFus67PIOenxmclbtt2dxcrdjwYl+APgWuNGyrz8065YkdOyA9csAL7b7WqN48eu0O+1GW3pjR3OCj2KITvY5FgwoMbjqFPglpq2GVYsNjwG2lQ89V09OWpkzfWjoiXQy8M+GEx8BRFMJ0SLVN70cSa5niJUIBWZwWGb6DOJyQ4aypo6ECGWvqN1ZB8TcNiRIMx9DtZ+cSPWtuV/BXGUIYIMgMzxSxT8IhAeOdMItIvr3syMCSNQln8DQXGrJ2Cid9yKitQzhomUESTQ0ShZgE9chGBKKpnRAjON067X3bh9453ekVE2WG2LHsy4pqxrDChD7LITY5A8W3ALt8KBPauasy+pwRJS++h1C8IspwMOc4P7noI8VvqOWAFww0ToNy+Rj744mg0dNoUMziQy5TzVBBJQx1HGCn+R6jMCAwEAAQ==
              -----END PUBLIC KEY-----

As next we show how to keep your private key inside a keystore.

Table 6. JWT Configuration configured using private and public key inside a keystore
Key M. Description

configseeder.server.security.auth.api-key.keystore.location

M

Location of the keystore (e.g. /keys/keystore.jks)

configseeder.server.security.auth.api-key.keystore.password

M

Password of the keystore

configseeder.server.security.auth.api-key.keystore.type

Type of the keystore. Defaults to JKS. Can be of type PKCS12 too.

configseeder.server.security.auth.api-key.credential.keystore-alias

M

Name of the private key inside the keystore.

configseeder.server.security.auth.api-key.credential.password

O

Passwort of the private key (if needed). Defaults to empty.

Example of an JWT private key configuration inside a keystore:

configseeder:
  server:
    security:
      auth:
        api-key:
          keystore:
            location: /keys/keystore.pkcs12
            password: not-set
            type: PKCS12
          credential:
            keystore-alias: jwt
            password: Wkd_892$qnsd2
3.1.2.7. Setup Authentication

The following ways to authenticate users can be configured:

  1. OIDC / OAuth2 (preferred)

  2. SAML

  3. Pre-Authenticated

All authentication mechanism can be activated at the same time.

3.1.2.7.1. OIDC / OAuth2 with an Identity Provider

It is possible to configure multiple OIDC / OAuth2 identity providers, although this is a rare case. While configuring you application, the <id> placeholder in the key can be any simple string (pattern [a-z]+).

Table 7. OIDC / OAuth2 Settings
Key M. Description

configseeder.server.security.auth.redirect-only-one

Set to true if you have only one IdentityProvider configured and you want to be immediately redirected to the login screen

configseeder.server.security.auth.email-unique

Set to true if email should be unique among all users, otherwise false. Default: true.

configseeder.server.security.auth.idp-roles-filter

Java regular expression which can be used for filtering relevant roles.

configseeder.server.security.auth.oauth2.enabled

M

Set to true if oauth2 should be used.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.username

Attribute name for the username. Defaults to: username. In may cases it’s not username, but login.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.email

Attribute name for the email. Defaults to: email.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.firstname

Attribute name for the first name. Defaults to: 'firstname'. Often: given_name

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.lastname

Attribute name for the last name. Defaults to: lastname. Often: family_name

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.first-name-last-name

Attribute name if first name and last name are sent together. Defaults to null.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.avatar

Attribute name for the avatar. Should contain the URL or the base64 encoded image. Defaults to avatar. Often it’s picture.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.locale

Attribute name for the language (only for initialization). Defaults to locale.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.access-token-role-paths

If the roles should be extracted from access token. Example: realm_access.roles. Separate by comma to support multiple paths.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.user-info-role-paths

If the roles should be extracted from the user info endpoint. Example: roles,application.roles. Separate by comma to support multiple paths.

configseeder.server.security.auth.oauth2.registrations.<id>.mapping.static-roles

Comma separated list of roles which should be assigned to all authenticated users. E.g. READER or DEVELOPER. See also chapter [configseeder-management-data-roles-examples].

configseeder.server.security.auth.oauth2.registrations.{registrationId}.additionalRequestParams

Map with additional request params which should be sent to the IDP. Example kc_login_hint: adfs

spring.security.oauth2.client.registration.<id>.client-id

M

Identification of the application provided by the Identity Provider

spring.security.oauth2.client.registration.<id>.client-secret

M

Secret of the application provided by the Identity Provider

spring.security.oauth2.client.registration.<id>.authorization-grant-type

Defaults to: authorization_code

spring.security.oauth2.client.registration.<id>.redirect-uri

M

URL where user will be redirected after authentication to the identity provider. Default value: {baseUrl}/login/oauth2/code/{registrationId}

spring.security.oauth2.client.registration.<id>.scope

M

Additional scopes. E.g. openid

spring.security.oauth2.client.registration.<id>.clientName

M

Which name will be used to show on the login screen

spring.security.oauth2.client.provider.<id>.authorization-uri

M

URI where user should be redirected to log in

spring.security.oauth2.client.provider.<id>.token-uri

M

URI where a the system can retrieve a token

spring.security.oauth2.client.provider.<id>.user-info-uri

URI where additional user details can be retrieved from the Identity Provider. This is needed if names, locale, roles or other user attributes should be retrieved

spring.security.oauth2.client.provider.<id>.jwk-set-uri

M for OIDC

URI to verify the token signature

spring.security.oauth2.client.provider.<id>.user-name-attribute

M

Attribute which should be used to fetch the username. Defaults to: preferred_username

See ConfigSeeder Management Examples for real world values.

3.1.2.7.2. SAML

It is possible to configure multiple SAML identity providers, although this is a rare case. While configuring your application, the <id> placeholder in the key can be any simple string (pattern [a-z\-]+).

Table 8. SAML Settings (Starting with configseeder.server.security.auth.saml2). M=mandatory, K=mandatory if keystore is used, E=mandatory if provided externaly using files or inlined.
Key M. Description

.. enabled

M

Should be set to true if saml2 should be used.

.. registrations.<id>.registration-id

M

Identifies the SAML registration configuration (and is als part of the URL)

.. registrations.<id>.keystore.location

K

Location of the keystore. Only mandatory if certificates or private keys are stored in a keystore.

.. registrations.<id>.keystore.password

K

Password of the keystore. Only mandatory if certificates or private keys are stored in a keystore.

.. registrations.<id>.type

Type of the keystore (Default: JKS)

.. registrations.<id>.identity-provider.entity-id

M

Identification on the IDP

.. registrations.<id>.identity-provider.singlesignon.url

M

SAML Single Sign On URL

.. registrations.<id>.identity-provider.verification.credentials.<n>.certificate-location

E

Locations of the X.509 certificate (PEM file) used for verification of incoming SAML messages. (Start with data: if you desire to inline the certificate.)

.. registrations.<id>.identity-provider.verification.credentials.<n>.keystore-alias

K

Alias of the certificate in the configured keystore.

.. registrations.<id>.signing.credentials.<n>.private-key-location

E

Private key (PEM file) used for signing or decrypting. (Start with data: if you desire to inline the certificate.)

.. registrations.<id>.signing.credentials.<n>.certificate-location

E

Relying Party X509Certificate (PEM file) shared with the identity provider. (Start with data: if you desire to inline the certificate.)

.. registrations.<id>.signing.credentials.<n>.keystore-alias

K

Alias of the private key in the configured keystore.

.. registrations.<id>.signing.credentials.<n>.password

Password of the private key (only supported for keys in the keystore)

.. registrations.<id>.attribute-mapping.username

Attribute name containing the username. Defaults to null. If not defined, the value from the subject will be taken.

.. registrations.<id>.attribute-mapping.first-name

Attribute name containing the firstname. Defaults to firstname.

.. registrations.<id>.attribute-mapping.last-name

Attribute name containing the lastname. Defaults to lastname.

.. registrations.<id>.attribute-mapping.email

Attribute name containing the email. Defaults to email

.. registrations.<id>.attribute-mapping.locale

Attribute name containing the locale. Defaults to locale.

.. registrations.<id>.attribute-mapping.role

Attribute name containing the role. Defaults to role.

Example configuration using external yaml:

configseeder:
  server:
    security:
      auth:
        saml2:
          enabled: true
          registrations:
            inlined:
              registration-id: configseeder-saml
              identity-provider:
                entity-id: https://auth.oneo.cloud/realms/config-server-review
                singlesignon:
                  url: https://auth.oneo.cloud/realms/config-server-review/protocol/saml
                verification:
                  credentials:
                    - certificate-location: file:/certificates/saml/idp.cer
              signing:
                credentials:
                  - private-key-location: file:/credentials/saml/private.key
                    certificate-location: file:/credentials/saml/certificate.cer
              attribute-mapping:
                first-name: givenName
                last-name: surname
                role: Role
            using-keystore:
              registration-id: configseeder-saml
              keystore:
                location: file:/credentials/saml-keystore.jks
                password: not-set
              identity-provider:
                entity-id: https://auth.oneo.cloud/realms/config-server-review
                singlesignon:
                  url: https://auth.oneo.cloud/realms/config-server-review/protocol/saml
                verification:
                  credentials:
                    - keystore-alias: idp-saml-certificate
              signing:
                credentials:
                  - keystore-alias: saml-private-key
                    password: SamlPassword!

See ConfigSeeder Management Examples for real world examples.

3.1.2.7.3. Pre-Authenticated

ConfigSeeder can be configured to trust pre-authenticated users over headers. This can be used where users are authenticated on a WAF with IAM support (e.g. Airlock) or Apache which is handling the authentication.

Note that currently at least the username and email header needs to be served. If username is not served, pre-authentication step will be skipped.

For these scenarios following configuration options are available:

Table 9. Pre-Authenticated Settings
Key M. Description

configseeder.server.security.auth.http-header.enabled

Set to true if you want to enable it. Defaults to false.

configseeder.server.security.auth.http-header.default-roles

Default roles if the header does not delivery any role. Defaults to empty.

configseeder.server.security.auth.http-header.ip-restrictions

Comma separated IPs, from where the server shall accept header authentication. Only active if at least one ip is entered.

configseeder.server.security.auth.http-header.mapping.username

Which header contains the username. Defaults to: x-auth-username.

configseeder.server.security.auth.http-header.mapping.email

Header containing the e-mail. Defaults to x-auth-email.

configseeder.server.security.auth.http-header.mapping.first-name

Header containing the firstname. Defaults to x-auth-firstname.

configseeder.server.security.auth.http-header.mapping.last-name

Header containing the lastname. Defaults to x-auth-lastname.

configseeder.server.security.auth.http-header.mapping.name

Header containing the name. Defaults to x-auth-name.

configseeder.server.security.auth.http-header.mapping.roles

Header containing the roles. Defaults to x-auth-roles.

If pre-authentication works, a call to user’s endpoint should return a 200:

curl -X GET "https://<host>/internal/user" -H "x-auth-username: john.doe" -H "x-auth-email: john.doe@mail.com" -H "accept: */*"
3.1.2.8. Configure Access Restrictions

ConfigSeeder distinguishes between functional permissions and data-based permissions. The functional permissions are grouped into six predefined function roles. The data-based permissions are defined at runtime by the configuration of the individual objects. These configurations enable a fine-granular authorization assignment, which can vary greatly depending on the intended use and can even become very complex. At this point, it is important to know which groups of people should have which authorizations.

In most of the cases it makes sense to apply at the beginning configurations based on one of the most matching scenario listed in chapter Access Scenarios and apply the documented configurations on the Role Mapping Configuration Page (https://<configseeder>/ui/role-management).

3.1.2.9. Other configurations

Here you will find all non-topic related configurations.

Table 10. Mail Settings
Key M. Description

spring.mail.host

Notification

SMTP Server Hostname

spring.mail.port

Notification

SMTP Server Port

spring.mail.username

Notification

SMTP Server Login (if needed)

spring.mail.password

Notification

SMTP Server Password (if needed)

spring.mail.properties.mail.smtp.auth

Notification

SMTP Authentication. Values: true or false

spring.mail.properties.mail.smtp.starttls.enable

Notification

SMTP Enable StartTLS. Values: true or false

Table 11. API Key
Key M. Description

configseeder.server.api-key.max-validity

Number of maximum days an API Key can be valid. Defaults to 2 years ( = 730 days)

Table 12. UI Settings
Key M. Description

server.port

Port on which the server listens (Defaults to 8080)

configseeder.server.ui.urls.logo

URL to the logo on the left top corner

configseeder.server.ui.calendar.firstDayOfWeek

Calendar: First day of week. 0 = Sunday, 1 = Monday

configseeder.server.ui.global-config.languages

Available languages in the UI splitted by comma (Defaults to all supported languages = de,en)

configseeder.server.ui.global-config.logging.client-log-level

Log level on client side. Defaults to INFO

configseeder.server.ui.global-config.logging.client-server-log-level

Which client logs are shown on server side. Defaults to INFO

Table 13. Notification Settings
Key M. Description

configseeder.server.notification.base-url

M

Url to ConfigSeeder which should be used for E-Mails. Typically just https://<configseederhost>/

configseeder.server.notification.sender

M

Sender of expiration notification

configseeder.server.notification. api-key-expiration.cron

Cron expression to set up scheduling of expiration notifications job (Defaults to 2 A.M.). CronSequenceGenerator

configseeder.server.notification. api-key-expiration.days-to-expire

Number of days before expiration date to send notification (Defaults to 14 days)

configseeder.server.notification. api-key-expiration.recipient-emails

List of email addresses where to send expiration notification separated by ,

configseeder.server.notification. api-key-expiration.notify-owner

Flag to notify owner of the (not generated / manually created) api key. Defaults to true

configseeder.server.notification. api-key-expiration.notify-owner-for-generated

Flag to notify owner of the (generated) api key. Defaults to false

configseeder.server.notification. certificate-expiration.cron

Cron expression to set up scheduling of expiration notifications job (Defaults to 2:15 A.M.). CronSequenceGenerator

configseeder.server.notification. certificate-expiration.days-to-expire

Number of days before expiration date to send notification (Defaults to 14 days)

configseeder.server.notification. certificate-expiration.notify-expiring-certificates

Flag to enable notifications for expiring certificates. Defaults to true

configseeder.server.notification. certificate-expiration.recipient-emails

List of email addresses where to send expiration notification separated by ,

Table 14. Miscellaneous
Key M. Description

configseeder.server.configurationvalue.version.stripQualifiers

If qualifiers like snapshot, rc, alpha, betta should be removed from the calling version. Defaults to false.

configseeder.server.configurationvalue.version.strippedQualifiers

Defaults to alpha, beta, milestone, rc, snapshot, sp

configseeder.server.action-log.enabled

If action logs should be collected. Default: true.

configseeder.server.action-log.log-reads

If action logs should log read operations. Default: true.

configseeder.server.action-log.log-writes

If action logs should log write operations. Default: true.

configseeder.server.action-log.cleanup.enabled

If action logs should be cleaned up. Default: true

configseeder.server.action-log.cleanup.cron

At which time action logs should be cleaned up. Defaults to 0 0 3 ? * *.

configseeder.server.action-log.cleanup.after

Cleanup action logs older than x days. Defaults to 61.

configseeder.server.action-log.cleanup.stop-after

Cleanup logs in junks and stop after x milliseconds. Defaults to 120_000. It’s also possible to use shortcuts: 2m or 120s or even 1h.

configseeder.server.action-log.cleanup.batch-size

Cleanup maximal x entries in a chunk. Defaults to 2_500.

configseeder.server.action-log.persistence.lazy

If action log should be persisted lazily. Defaults to true.

configseeder.server.action-log.persistence.lazy-initial-delay

Initial delay for the first persistence operation in milliseconds. Defaults to 10_000.

configseeder.server.action-log.persistence.lazy-fixed-rate

How often lazy persistence should be performed in milliseconds. Defaults to 10_000.

configseeder.server.setup.login.credential-needed

If a credential needs to be passed when running in setup mode and creating a setup user. Defaults to true

configseeder.server.setup.login.credential

A default credential, if not a new credential shall be generated on each startup. Default: `` (empty ⇒ new one will be generated).

Table 15. Miscellaneous (from Spring)
Key Description

spring.servlet.multipart.max-file-size

Maximum upload file size

spring.servlet.multipart.max-request-size

Maximum request size

3.1.3. Run ConfigSeeder Management

3.1.3.1. Java

Pass sensitive value as environment variables:

export SPRING_DATASOURCE_PASSWORD="..."
export SERVER_SSL_KEYSTOREPASSWORD="..."
export CONFIGSEEDER_SERVER_SECURITY_AUTH_APIKEY_PRIVATEKEY="..."
export CONFIGSEEDER_SERVER_SECURITY_AUTH_APIKEY_PUBLICKEY="..."
export CONFIGSEEDER_SERVER_SECURITY_CONFIGURATIONVALUE_ENCRYPTION_SECRET="..."
java -Xms512M -Xmx512M -jar configseeder-server-management-app--2.25.4.jar

You may pass them also as parameters, but then it would be visible with ps / cat /proc/<pid>/cmdline:

java -Xms512M -Xmx512M -jar configseeder-server-management-app--2.25.4.jar \
  --spring.datasource.password=... \
  --server.ssl.key-store-password=... \
  --configseeder.server.security.auth.api-key.private-key=... \
  --configseeder.server.security.auth.api-key.public-key=... \
  --configseeder.server.security.configurationvalue.encryption.secret=...

Add --spring.config.location=/<path>/application.yaml to the command line if the configuration file is not in the same place as the jar-file.

3.1.3.2. Docker
docker run -d -it \
  -v `pwd`/application.yaml:/application.yaml \
  -e JAVA_OPTS="-Xms512M -Xmx512M" \
  --name configseeder \
  -p 8080:8080 \
  configseeder/management-h2:2.25.4-alpine

You may pass secrets as environment parameters, but then it would be visible with ps / cat /proc/<pid>/cmdline. We strongly disadvise.

3.1.4. Access Restriction

As mentioned in chapter Configure Access Restrictions ConfigSeeder distinguishes between functional permissions and data-based permissions. Let’s dive more into how these permissions work.

3.1.4.1. Data Based Permissions

Based on the security configurations of the Tenant, Environment and Configuration Group objects, ConfigSeeder generates the required data authorization roles. It is important to understand that data access configurations are inherited to dependent entities.

SecurityEntityModel

Each of the secured entities can be configured at least with following two attributes:

  • Secured Type: Restricts access based on the type of operation:

    • DERIVED: Same rule applies for the most restrictive parent object, access is granted

    • W_ROLE: Read access is drived from the parent object. Dedicated role needed for write operations.

    • RW_ROLE: One dedicated role is needed to perform read and write operations.

    • SEPARATE_RW_ROLES: Separate roles for read and write are needed.

  • Secured Role Infix: Role Name Fraction used.

The generated data roles are additionally influenced by the configurations listed bellow. We suggest to keep them as they are:

Table 16. Data Based Permission Configuration
Key M. Description

configseeder.server.security.roles.prefix

Prefix for all data access roles. Defaults to cs

configseeder.server.security.roles.infix-separator

Separator character. Defaults to _

configseeder.server.security.roles.read-suffix

Suffix for all read-specific data roles. Defaults to r

configseeder.server.security.roles.write-suffix

Suffix for all write-specific data roles. Defaults to w

configseeder.server.security.roles.secured-infix

Infix for all secured marked configuration values. Defaults to sec

configseeder.server.security.roles.all-infix

Role suffix for having privileges for all sub entities. Defaults to all

Based on the configurations and the security settings of each object a role is generated in following format:

ROLE_PATTERN = ${configseeder.server.security.roles.prefix} + "_" + OBJECT_PATH + SECURED + SUFFIX (1)
OBJECT_PATH = [ TENANT_INFIX + "_" | ] + [ CONFIGURATION_GROUP_INFIX + "_" | ] + [ ENVIRONMENT_INFIX + "_" | ] (2)
SECURED = [ ${configseeder.server.security.roles.secured-infix} + "_" | ] (3)
SUFFIX = [ ${configseeder.server.security.roles.read-suffix} | ${configseeder.server.security.roles.write-suffix} | ] (4)
1 The minimal ROLE_PATTERN path is ${configseeder.server.security.roles.prefix}.
2 If securedType != DERIVED, then the configured infix of the object is used
3 If the configuration value is marked with secured = true, then the infix will be added
4 If at least one object has securedType == SEPARATE_RW_ROLES and access mode is READ then the ${configseeder.server.security.roles.read-suffix} is used.
If at least one object has securedType == W_ROLE || securedType == SEPARATE_RW_ROLES and access mode is W_ROLE then the ${configseeder.server.security.roles.write-suffix} is used.
The ${configseeder.server.security.roles.all-infix} (defaults to all) gives access to the entity and sub entities.

You can find in the appendix the chapter [configseeder-management-data-roles-examples] with more details.

3.1.4.2. Function Based Permissions

ConfigSeeder offers by default a set of function roles.

Table 17. Default Function Role Privilege Mapping
Function Role Description

SUPERADMIN

Administrator with all permissions. Should only be assigned to a limited number of users.

LICENSEMANAGER

License manager can only change the license.

TENANTADMIN

Sub-administrator, who is responsible for the administration of ConfigSeeder, but without license management. Has authorization to change environments and configuration groups.

APPLICATIONMANAGER

Can modify configuration groups and values, but no environments.

DEVELOPER

Can modify configurations, but no environments or configuration groups.

READER

Read only access.

OPERATOR

Operator only access

For more details which privileges they contain, see [configseeder-management-function-roles-complete].

3.1.5. Access Scenarios

3.1.5.1. One Team with access to everything

Scenario: ConfigSeeder is just used by one team which has access to everything.

Table 18. Object Configuration (One Team)
Object Security Config

Configuration Group

  • Secured Type: inherit

  • Secured Values: inherit

Environment

  • Secured Type: inherit

Configuration Value

  • Secured: false (default)

Table 19. Role Mapping (One Team)
Name Identity Provider Role Function Role Data Role

Admin

Admin

  • SUPERADMIN

  • cs_all

Devops

User

  • APPLICATIONMANAGER

  • DEVELOPER

  • cs_all

3.1.5.2. DEVOPS by Application

Scenario: Administrator Role and independent DEVOPS Team with full access.

Table 20. Object Configuration (DEVOPS Teams)
Object Security Config

Configuration Group

  • Secured Type: read or write

  • Secured Role Infix: devopsteam1

  • Secured Values: inherit

Environment

  • Secured Type: inherit

Configuration Value

  • Secured: false (default)

Table 21. Role Mapping (DEVOPS Teams)
Name Identity Provider Role Function Role Data Role

Admin

Admin

  • SUPERADMIN

  • cs_all

DevOpsTeam1

DevOpsTeam1

  • APPLICATIONMANAGER

  • DEVELOPER

  • cs

  • cs_devopsteam1_all

3.1.5.3. Application Management and Sub-Organizations

Scenario: An application management team is responsible for all productive installations. Each suborganization has couple of leaders, which are also able to edit production values. Developers can only see production values, which are not secured. We assume that production environment is called prod.

Table 22. Object Configuration (Org Teams)
Object Security Config

Configuration Group

  • Secured Type: read or write

  • Secured Role Infix: org1

  • Secured Values: read or write

Environment

All exl. PROD:

  • Secured Type: inherit

PROD environment:

  • Secured Type: read and write

  • Secured Role Infix: prod

Configuration Value

  • Secured: true (if secrets should not be shown to the developers)

Table 23. Role Mapping (Org Teams)
Name Identity Provider Role Function Role Data Role

Admin

Admin

  • SUPERADMIN

  • cs_all

Leader Org1

Org1Lead

  • APPLICATIONMANAGER

  • DEVELOPER

  • cs

  • cs_org1_all

Developer Org1

Org1Developer

  • DEVELOPER

  • cs

  • cs_org1

  • cs_org1_pp_r

3.1.6. Example ConfigSeeder Management Configurations

GitHub OAuth2 Configuration (Spring Boot Hint: CommonOAuth2Provider)
spring:
  security:
    oauth2:
      client:
        provider:
          github:
            authorization-uri:        https://github.com/login/oauth/authorize
            token-uri:                https://github.com/login/oauth/access_token
            user-info-uri:            https://api.github.com/user
        registration:
          github:
            client-id:                0a71bb6b524870cb4ba1
            client-secret:            ff2a25830899fb40115cd720f01b575858959687
            authorization-grant-type: authorization_code
            redirect-uri:             '{baseUrl}/login/oauth2/code/{registrationId}'
            scope:                    read:user,user:email
            client-name:              GitHub
configseeder:
  server:
    security:
      auth:
        oauth2:
          registrations:
            github:
              mapping:
                first-name-last-name:     name
                username:                 login
                static-roles:             TENANT_ADMIN,DEVELOPER,cs  (1)
1 As GitHub brings no role with, we need to statically assign some roles to each user
Keycloak OIDC Configuration
spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            authorization-uri:        https://keycloak.oneo.ch/realms/config-server/protocol/openid-connect/auth
            token-uri:                https://keycloak.oneo.ch/realms/config-server/protocol/openid-connect/token
            user-info-uri:            https://keycloak.oneo.ch/realms/config-server/protocol/openid-connect/userinfo
            jwk-set-uri:              https://keycloak.oneo.ch/realms/config-server/protocol/openid-connect/certs
            user-name-attribute:      preferred_username
        registration:
          keycloak:
            client-id:                oneo-config-server
            client-secret:            oneo-config-server
            authorization-grant-type: authorization_code
            redirect-uri:             '{baseUrl}/login/oauth2/code/{registrationId}'
            scope:                    openid,profile
            client-name:              Keycloak
configseeder:
  server:
    security:
      auth:
        oauth2:
          registrations:
            keycloak:
              mapping:
                firstname:                given_name
                lastname:                 family_name
                username:                 preferred_username
                avatar:                   avatar
                access-token-role-paths:  realm_access.roles
Keycloak SAML2 Configuration
configseeder:
  server:
    security:
      auth:
        saml2:
          enabled: true
          registrations:
            inlined:
              registration-id: configseeder-saml
              identity-provider:
                entity-id: https://auth.oneo.cloud/realms/config-server-review
                singlesignon:
                  url: https://auth.oneo.cloud/realms/config-server-review/protocol/saml
                verification:
                  credentials:
                    - certificate-location: |
                        data:-----BEGIN CERTIFICATE-----
                        MIICtzCCAZ8CBgFlJDeArTANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRjb25m
                        aWctc2VydmVyLXJldmlldzAeFw0xODA4MTAxNDIwNDFaFw0yODA4MTAxNDIyMjFa
                        MB8xHTAbBgNVBAMMFGNvbmZpZy1zZXJ2ZXItcmV2aWV3MIIBIjANBgkqhkiG9w0B
                        AQEFAAOCAQ8AMIIBCgKCAQEAg/Kh84sUzV2uHIdYmaRV10AOd7UCjLL0216wzVxE
                        e6jTLacjhBoLDPMqv6T53YH2DJkqoB97vmsvAePE3jy4yyTqPi4JQWcMDc+OU719
                        Y41VioyFLQaKuLP6FqkJfBdOsdwQHfxNwzLow6ULe7+rhdSMmBpPMfk61TY2OcbG
                        sW7sGFckXEqQn5IlQ5X/QuB9ZdT/9kvQfgIK7ySm+LkrH6z7wcgGfYaZaqvxHxqO
                        GdmVrOVoty5hV1iZ52I4EZ3F8Pite8GVD/YuJxEmTInhlxhOLZ/OYxFCIiYIIEGU
                        qyDG9dDdznD4ZwnfghtCqdh80gik7mc1v52WfY7r9W+8HwIDAQABMA0GCSqGSIb3
                        DQEBCwUAA4IBAQBsHKEidQNU+FAF1xabsO7U159bMnDE/D0PiYFzbox+g3IH1J3R
                        sCTtzbpKKUSY5z8vcbzfbNtEjko8x1esYjVuWLddg0kFZL2SQ5mOcmUtYACz7PNL
                        lF9GeWIaHWgqr8Bj8HjTCc4CcmziIOYCbgugNkKS5WK3flismxsIBKCO/cKl4kZ3
                        lWRpcNCG2vIU0e0+1OMwJdqSh5zPnPC35jdUxfPynvsytqN9yYE8FOces9B6tXvB
                        TNiBDZiCHhKID4WjqfTUTzus515FbvVSNcIEF5iSXL2BRUkWF2QRZ29hnw5sPa+C
                        uDLGLRyovkAxBC/4JNu6JQl4uVaoNJ+5jGgT
                        -----END CERTIFICATE-----
              signing:
                credentials:
                  - private-key-location: |
                      data:-----BEGIN PRIVATE KEY-----
                      MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA9vfIeUyWSF9w
                      0U+7Fz9j1TdDSNqqFDBcyVG4GvbCKRBDTjYQZLaf0nh/rDPUCYKq1z3ruHsz8nKA
                      BfgIqFWBeWv8tp1caRRxDGZXLIDy+W0kNOa1fdJ7ePDmwBFgGsdvY72tGiIrpsh0
                      V7Z+y4YJ7CYsq7MbwFb/krHVM3uRpMvv7lpfwg06sYFt3CYVvf4oTzALx0yOAa/g
                      AlJh/KJ9YATQKMLM5XQ/NrybTGeRLsQjLHL3YU8QRZhL3+yHMDFfTwAsTGZicUAW
                      xIARak3IPq0qEkmpgl90Yxr0ZBllZL7836433d1VXdnzhUIyG7ZRbr8kW12lei0i
                      6dUl5EBtAgMBAAECggEATW2LxWRux8qoiUNjw8WzSOLQNu5v11kcgs7BPKeNJiGG
                      BgvHA9L1uLGialB1x3o9dUHC9efjf9ej/BzpBshM7xQCsbew8tizPB1mn0obP+O3
                      2JRlkqO3JpZDy8N79V0pc1fJT0EyPuPAgXaEouIVmbupIbmPt+34urppIwxyY+DR
                      2NEtNrirVDfdggti3zD5mxJX0RFVx2XiWnuCkHDJbjjNiCUCoZHJ5LDu1Vz3GAPX
                      a2PHfOP9WjA0iIxB0W/9WD1Q0mOP15wXO4cjkrcFUHWULZswCUFN6POjWJb18etZ
                      9RdXg981KA0PGeJwrkr7l4X16o+JvcPevDWuASqwAQKBgQD6PGyFu/myrJkBeLSa
                      aV+OXwNZZHldDhu1L7VoV2FcLv/Z0hDERzpylz3g7T4XcWefEYSdx2CqFtT1BGPf
                      yxem9ej2r7SCu4X/J1TAN+01rsnswqujlast/jwusPEPPuJRAVAYjWLk22ISO3CF
                      Q9FwU1Er6v9Sn7NGsGOo79VsbQKBgQDFaNSCXuY12H0BkdRt7WtY4Sg4Kh8Itzmx
                      SJhohzNk7ChPqiTbNTRcs9S6A3jqfc71LjTOR2aTpPlgrNPThAPCOE777cSjekFy
                      zSp2/CEqrcTiLDL9JIDmp9p2Mh5AtM1ozyCtnKFV9W4qEmMKWwDr8vuseLDSYbU3
                      dfnZ3V2kAQKBgQCLf9nj5/n4oH2fs2HDXxD1NoSQdhGuTaompNUVuPi+wjjBfnj4
                      YtjzftrxJHDj8sWYiNSMfNSnnZjUisD+xqKF4jsgIq2X3+CAcLWvfr4PmkRZZ+D1
                      jf9i4YM9aEjKffz+uaOkPFWwTZjrnxVIRZlhbCEpQZVaw0rXEohMan9pNQKBgCPh
                      kf7AUQeoedA3OW1hogldo3nR7iIfa5UVpN4a9PR7GDiirGBLo2DWLuxAhVHZ3Cwk
                      YeS8haCAc/NmdnoTnLtIkNKpvPwZ1JuRPw6xdTXUr9yMPBhW8jb6e3LQWT9ZnZxX
                      SZFEzu5Y1dU/0lCnidtZJvWKWlli+RVDlE5NNiABAoGBAPNPIB0cS8kljhUztDym
                      YLXqSorVZxo8Q+vRUZKL6kSDxqBIjK+KmZevzLpteOj9r94B6leC6P6FmHCfFr00
                      XSbQSjIl9EtivxED+u39TYWo6HgZ5hTzeVFy4Yaj5plpUOoRN9JVY42LRlkYtMl1
                      zOowPdBvH700jEjLiv7LY3lO
                      -----END PRIVATE KEY-----
                    certificate-location: |
                      data:-----BEGIN CERTIFICATE-----
                      MIIDFTCCAf0CBgFx6OUFdzANBgkqhkiG9w0BAQsFADBOMUwwSgYDVQQDDENodHRw
                      Oi8vbG9jYWxob3N0OjgwODAvc2FtbDIvc2VydmljZS1wcm92aWRlci1tZXRhZGF0
                      YS9rZXljbG9hay1vbmVvMB4XDTIwMDUwNjA3MjkxNVoXDTMwMDUwNjA3MzA1NVow
                      TjFMMEoGA1UEAwxDaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbWwyL3NlcnZpY2Ut
                      cHJvdmlkZXItbWV0YWRhdGEva2V5Y2xvYWstb25lbzCCASIwDQYJKoZIhvcNAQEB
                      BQADggEPADCCAQoCggEBAMD298h5TJZIX3DRT7sXP2PVN0NI2qoUMFzJUbga9sIp
                      EENONhBktp/SeH+sM9QJgqrXPeu4ezPycoAF+AioVYF5a/y2nVxpFHEMZlcsgPL5
                      bSQ05rV90nt48ObAEWAax29jva0aIiumyHRXtn7LhgnsJiyrsxvAVv+SsdUze5Gk
                      y+/uWl/CDTqxgW3cJhW9/ihPMAvHTI4Br+ACUmH8on1gBNAowszldD82vJtMZ5Eu
                      xCMscvdhTxBFmEvf7IcwMV9PACxMZmJxQBbEgBFqTcg+rSoSSamCX3RjGvRkGWVk
                      vvzfrjfd3VVd2fOFQjIbtlFuvyRbXaV6LSLp1SXkQG0CAwEAATANBgkqhkiG9w0B
                      AQsFAAOCAQEALLbQl5LZxRVFekfW9F2+u6FLNtEWC9nZBbwK9gNjum+DTWC8QaJM
                      MTw3C4cBTitH4bBktaVZMrFa2jZVkDIBiNcvxtQQcwJUbVGMrkxDbmfAHCyaUkfp
                      ZbOBOnQ7ZgdtraIm8YTz3Bc8lRg2rBfpWlru3DSeXCYk9u+KpPMA5FNRfI241sQ2
                      U2kHgxaJqASQ5td3rmbancwVAOTH1hIwuvFAiPC4rFzqiZC+d+0ACVLpx47zFnjG
                      mrJmNup8hy7cDp7cV7bneBxpIxHLOf2+HVNHB+xNeYQ7qR5L2x7c8JcvilaUdQm5
                      mPlRc4FtLLR/Ngyn32EhkIoCDfEtuhP6GQ==
                      -----END CERTIFICATE-----
              attribute-mapping:
                first-name: givenName
                last-name: surname
                role: Role

3.1.7. Tooling

3.1.7.1. Health Details

Reports about health status of ConfigSeeder can be retrieved on:

$ curl 'https://<your-host-name>/actuator/health' -i -X GET
3.1.7.2. Access Metrics

Get access to ConfigSeeder metrics, especially number of requests on the configuration endpoint:

curl 'https://<your-host-name>/actuator/metrics/configseeder.external.timer/' -i -X GET

Access for specific tags (e.g. environment PROD):

curl 'https://<your-host-name>/actuator/metrics/configseeder.external.timer/?tag=environment%3APROD' -i -X GET
3.1.7.3. Change Log Levels

As ConfigSeeder is a SpringBoot Application, log levels can be changed at runtime. For this use:

$ curl 'https://<your-host-name>/actuator/loggers/com.configseeder' -i -X POST \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -d '{"configuredLevel":"debug"}'

Clear your settings to your default:

$ curl 'http://<your-host-name>/actuator/loggers/com.example' -i -X POST \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -d '{}'

3.1.8. Trouble Shooting

3.1.8.1. OIDC

If you encounter any issue for setup OIDC, change loglevel for security to TRACE. This can be performed with:

logging.level.com.configseeder.infrastructure.security=TRACE
  • [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for "<host>": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

    ⇒ The SSL certificate is not known to ConfigSeeder. Probably it’s a self-signed certificate or the root ca is not known to ConfigSeeder.

  • OAuth2AuthenticationException: [invalid_token_response]: Operation timed out (Connection timed out)

    ⇒ Most probably the connection to the OIDC system is blocked by firewall or missing proxy configuration (e.g. -Dhttps.proxyHost= and -Dhttps.proxyPort= and -Dhttps.proxySet=true)

  • org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_client]

    ⇒ Missing configuration for client-id or client-secret

3.2. Value Provider (HA)

3.2.1. Requirements

  • System (per Value Provider instance)

    • minimal: 512MB; recommended: 2GB

    • minimal: 1 core; recommended: >= 2 cores

    • JRE 17 (We recommend Zulu)

  • LoadBalancer (if multiple Value Providers will be used)

3.2.2. Setup Steps

3.2.2.1. Preparations

Prepare before configuring ConfigSeeder ValueProvider:

  1. Decide if a HA store should be used (no HA store → each ValueProvider synchronizes on it’s own, ETCD. Please be advised, without an ETCD cluster, HA can not be achieved for some scenarios.)

  2. ConfigSeeder Management is running

3.2.2.2. Configure

Configurations can be passed in three variants and even mixed. We suggest splitting up configurations as follows:

  • Non-sensitive values: in a file called application.yaml

  • Sensitive values as environment variables

Follow these steps for the installation:

3.2.2.3. SSL Certificates

By default the application runs on port 8080 without encryption. You can easily force SSL connection by providing a SSL certificate. Follow SSL Certificate if you don’t know how to create a certificate.

Table 24. SSL Configuration
Key M. Description

server.port

Port on which ConfigSeeder should be running. For SSL we suggest port 8443 or 443

security.require-ssl

Set to true if you want to enforce SSL

server.ssl.key-store-type

Your keystore format. Our example uses PKCS12

server.ssl.key-store

Path to your keystore. E.g. /etc/configseeder/keystore.p12

server.ssl.key-store-password

Password to your keystore. In our example: changeit

server.ssl.key-alias

If more than one certificate is defined, use the alias or number. In our example: configseeder

3.2.2.4. Encryption Keys

ConfigSeeder ValueProvider can use the same encryption keys as the management instance, but we recommend against. We strongly recommend to set your own values.

Table 25. Encryption Configuration
Key M. Description

configseeder.server.security.configurationvalue.encryption.secret

M

Encryption key to be used to encrypt the configuration values. Generate using AesKeyGenerator

3.2.2.5. JWT Public Key

In order to verify the validity of the API keys, the ConfigSeeder ValueProvider needs to know the public key of the JWT.

Table 26. JWT Configuration configured using PKCS8 PEM files
Key M. Description

configseeder.server.security.auth.api-key.credential.public-key-location

M

Public key als PKCS8 PEM file. Either the location needs to specified (e.g. /keys/jwt.pem) or the content can be inlined using the data:-prefix before the value.

Example of an inlined JWT public key:

configseeder:
  server:
    security:
      auth:
        api-key:
          credential:
            public-key-location: |
              data:-----BEGIN PUBLIC KEY-----
              MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1DJw8nctvrHlP3udyGHLZNBJqQJ0jwCiX3Pcg66JK5xrVtxScz44d9cE1SdnVi4agACrhR+uQyjh13BP70kXOSQItHWH1r7k7kHxtSDbOVz/JPzlVfE96m99VgwmiYRjDDtfqWoDY+hV1b1gKFZ32AJ2I1fAJtMrtMerB4waPWz99Ym+L+kgKFwseISNEoTMTNvuVlbf5SKLS3UGRnipnEbRZi8Saj4jeywFjZaCGlFsRMiErAw9zu+C5GU8HwKsq31loFus67PIOenxmclbtt2dxcrdjwYl+APgWuNGyrz8065YkdOyA9csAL7b7WqN48eu0O+1GW3pjR3OCj2KITvY5FgwoMbjqFPglpq2GVYsNjwG2lQ89V09OWpkzfWjoiXQy8M+GEx8BRFMJ0SLVN70cSa5niJUIBWZwWGb6DOJyQ4aypo6ECGWvqN1ZB8TcNiRIMx9DtZ+cSPWtuV/BXGUIYIMgMzxSxT8IhAeOdMItIvr3syMCSNQln8DQXGrJ2Cid9yKitQzhomUESTQ0ShZgE9chGBKKpnRAjON067X3bh9453ekVE2WG2LHsy4pqxrDChD7LITY5A8W3ALt8KBPauasy+pwRJS++h1C8IspwMOc4P7noI8VvqOWAFww0ToNy+Rj744mg0dNoUMziQy5TzVBBJQx1HGCn+R6jMCAwEAAQ==
              -----END PUBLIC KEY-----
3.2.2.6. Synchronisation

In order that the ConfigSeeder ValueProvider is capable of retrieving the values, a synchronization API Key needs to be created in ConfigSeeder Management.

Ensure that the privilege Synchronisation is checked (a synchronisation API key can only be generated by SuperAdmin).

Table 27. API Key Configuration
Key M. Description

configseeder.server.ha.synchronization.enabled

M

true by default

configseeder.server.ha.synchronization.client.baseUrl

M

ConfigSeeder Management base url, e.g. http://localhost:8080/

configseeder.server.ha.synchronization.client.apiKey

M

API Key from ConfigSeeder Management, which should be used for synchronisation

configseeder.server.ha.synchronization.client.connectTimeOut

Connect timeout. Defaults to 3sec

configseeder.server.ha.synchronization.client.readTimeout

Read timeout. Defaults to 10sec

configseeder.server.ha.synchronization.client.writeTimeout

Read timeout. Defaults to 10sec

configseeder.server.ha.synchronization.client.proxy.hostname

Proxy hostname

configseeder.server.ha.synchronization.client.proxy.port

Proxy port

configseeder.server.ha.synchronization.initialDelay

Defaults to 1000 msec (in msec)

configseeder.server.ha.synchronization.fixedRate

Defaults to 5000 msec (in msec)

configseeder.server.ha.synchronization.fullSyncAfter

Defaults to 15m (in Duration, e.g. 15m, 30s)

configseeder.server.ha.synchronization.filter.tenantKeys

Filter to include only specific tenants (tenant keys, separated by , or in YAML as a list)

configseeder.server.ha.synchronization.filter.environmentKeys

Filter to include only specific environments (environment keys, separated by , or in YAML as a list)

3.2.3. Run ConfigSeeder ValueProvider

3.2.3.1. Java
java -Xms512M -Xmx512M -jar configseeder-server-ha-configuration-provider-2.25.4.jar \
  --configseeder.server.ha.etcd.password=... \
  --server.ssl.key-store-password=... \
  --configseeder.server.security.auth.api-key.private-key=... \
  --configseeder.server.security.auth.api-key.public-key=... \
  --configseeder.server.security.configurationvalue.encryption.secret=...

Add --spring.config.location=/<path>/application.yaml to the command line if the configuration file is not in the same place as the jar-file.

3.2.3.2. Docker
docker run -d -it \
  --mount type=bind,source="$(pwd)"/application.yaml,target=/application.yaml \
  -e JAVA_OPTS="-Xms512M -Xmx512M"
  -e SERVER_SSL_KEYSTOREPASSWORD=... \
  -e CONFIGSEEDER_SERVER_SECURITY_JWT_PRIVATEKEY=... \
  -e CONFIGSEEDER_SERVER_SECURITY_JWT_PUBLICKEY=... \
  -e CONFIGSEEDER_SERVER_SECURITY_CONFIGURATIONVALUE_ENCRYPTION_SECRET=... \
  --name configseeder \
  -p 8080:8080 \
  configseeder/ha-configuration-provider:2.25.4

3.3. ConfigSeeder Clients

3.3.1. Common Setup Steps

All configuration parameters can be passed using a YAML file and be overwritten by system environment variables or start parameters. You can create your configuration manually by following this manual or generate it in ConfigSeeder Management by clicking on the Export button in the configuration management screen.

It’s strongly suggested to pass all parameters that may be different per environment as a parameters and not part of your artifact.
Although it’s possible to have API Keys part of a configuration file, we discourage your saving ApiKey in your file and persist it into your repository.
  1. Create a configseeder.yaml file containing all static configuration without any sensitive values (e.g. like API-Keys), dynamic values (like Hostname or Environment).

    At least configure the environment and one configuration. A minimal configuration looks like this:

    configseeder.yaml
    configseeder:
      client:
        environmentKey: "DEV"
        configurationGroupKeys: "demo"

    It is also possible to configure more settings. A full blown configuration example can be found bellow. Note that . For full reference of all configurations, please refer chapter All available configuration values.

    configseeder.yaml
    configseeder:
      client:
        tenantKey: "default"
        environmentKey: "DEV"
        version: "2.0.24"
        context: "local"
        configurations:
        - key: "group1"
          selectionMode: "LATEST"
        - key: "group2"
          version: 2
        initializationMode: "EAGER_ASYNC"
        refreshMode: "LAZY"
        refreshCycle: 15000
        retryWaitTime: 1000
        maxRetries: 3
        connectionTimeout: 500
        readTimeout: 2000
        serverUrl: "https://config-server-demo.oneo.ch"
        apiKey: "eyJ....KEUg0o"

The keys configurations as well as configurationGroupKeys specifies which configuration group(s) should be loaded. If the configuration is done with configurationGroupKeys, the client will always load the latest version. When using configurations it can be specified per configuraiton group which version should be retrieved.

  1. Pass all other mandatory parameters (e.g. dynamic or sensitive data like config server host name and API Key) as java parameters or environment variables.

    Example for Java Clients
    java -Dconfigseeder.client.serverUrl=https://demo-config-server.oneo.cloud/ -Dconfig-client.api-key=eyJ....KEUg0o -jar <yourapp>.jar

3.3.2. Best practices

  • Always pass API-Key as Java parameter or environment variable.

  • On non-dev instances pass also hostname as parameter instead of fixed configured in configseeder.yaml

  • If you pass the version of your application as config server argument, make sure configseeder.yaml get’s filtered and use variable @project.version@ in the file. Example:

    configseeder:
      client:
        version: "@project.version@"
  • If your application will not work with new values from ConfigSeeder received at runtime, just configure the refreshMode with NEVER

3.3.3. All available configurations

Configs are prefixed with configseeder.client and environment configs with CONFIGSEEDER_CLIENT. Example: configseeder.client.serverUrl and CONFIGSEEDER_CLIENT_SERVERURL.
Table 28. ConfigServerClient Paramenters
Config Environment Variable Mand./Opt Description

..serverUrl

__SERVERURL

M

URL of the config server

..apiKey

__APIKEY

M

API Key

..tenantKey

__TENANTKEY

O

Key of the tenant

..environmentKey

__ENVIRONMENTKEY

M

Key of the environment

..version

__VERSION

O

Filter: Version of the application

..context

__CONTEXT

O

Filter: Context (newly Labels, multiple Labels separated by comma)

..dateTime

__DATETIME

O

Filter: Date Time (Default: now())

..configurations.<n>.key

__CONFIGURATIONS_<n>_KEY

(M)

Name of the configuration group

..configurations.<n>.selectionMode

__CONFIGURATIONS_<n>_SELECTIONMODE

O

If there are multiple versions: LATEST, LATEST_RELEASED

..configurations.<n>.version

__CONFIGURATIONS_<n>_VERSION

O

Instead of selectionMode choose explicitly a version

..configurationGroupKeys

__CONFIGURATION_GROUP_KEYS

(M)

This key can be used instead of the configurations-Keys. Used selection mode is LATEST

..initializationMode

__INITIALIZATIONMODE

O

How all values should be initialized. Following options exists:

- EAGER: Loads values on startup of the application synchronously (blocking)

- EAGER_ASYNC: Loads values on startup with a separate thread (async) (default)

- LAZY: Loads values only on first request (blocking)

..refreshMode

__REFRESHMODE

O

When the values shall be refreshed:

- TIMER: Loads the values regularly (default)

- MANUAL: Application needs to call ConfigClientProvider.refreshValues() manually

- LAZY: Only on request

- NEVER: Never loads and updates the values. Same as MANUAL.

..refreshCycle

__REFRESHCYCLE

O

How often values get refreshed (in milliseconds, default: 15000)

..retryWaitTime

__RETRY_WAIT_TIME

O

How many milliseconds should be waited before a new request is performed (default: 500)

..maxRetries

__MAX_RETRIES

O

How many times connection retry should be performed (default: 3)

..connectionTimeout

__CONNECTIONTIMEOUT

O

How long the client shall wait for initial connection (default: 500)

..readTimeout

__READTIMEOUT

O

How long an answer from the server shall take max (default: 2000)

..disable

__DISABLE

O

Disable configseeder client with true

..recoveryFile

__RECOVERYFILE

O

If defined the given file will be created to store the latest answer from Config Seeder Management. On the next startup, if Config Seeder Management is not reachable, file contents will be used. If it’s not a file but a folder, a dedicated .configseeder-recovery.json file will be created to store the contents.

3.3.4. Curl

Any configuration can be downloaded with curl and an appropriate API Key from ConfigSeeder. The curl command can be generated in ConfigSeeder Management on the Export view.

For completeness here an example curl statement (it is also possible to retrieve configuration data depending on date or labels):

curl -X POST -H "Content-type: application/json" -H "Accept: text/x-java-properties" -d '{
  "tenantKey": "default",
  "environmentKey": "DEV",
  "version": "15.1.0",
  "configurations": [
    {
      "configurationGroupKey": "group1"
    }
  ]
}' 'https://<server>/external/configurations?download=false'

Following accept types are supported:

  • text/x-java-properties (Java Properties Files)

  • text/yaml (YAML Files)

  • application/x-ini (Windows INI Files)

  • text/csv (CSV Files)

  • application/json (Fully fledged detail)

3.3.5. Eclipse Micro Profile Config

3.3.5.1. Requirements
  • Application Server supporting Eclipse Microprofile Config >= 1.1.

  • Download matching version of config-client-microprofile-config-*.jar from our maven repository: https://maven.configseeder.com/

3.3.5.2. Configuration

Add config-client-microprofile-config-*.jar as dependency:

pom.xml

  com.configserver.client
  configseeder-client-microprofile-config
  2.25.4
3.3.5.3. Usage of the configuration values

Following examples show how you can inject a value at startup. Note that this value will never change anymore, although the may have been altered on the ConfigSeeder Management.

StaticExample.java
// String value
@Inject
@ConfigProperty(name = "BAR", defaultValue = "default value if BAR is not provided")
String bar;

// Boolean value
@Inject
@ConfigProperty(name = "BOOL_PROP", defaultValue = "no")
boolean boolProp;

If you want to retrieve your values at runtime, just inject Config and retrieve the value each time.

DynamicExample.java
@Inject
Config config;

@Inject
@ConfigProperty(name = "server.host.name")
javax.inject.Provider<String> hostName;

private void doSomeOperation() {
   // dynamic optional value
   Optional<String> proxy = config.getOptionalValue("mail.proxy", String.class);

   // dynamic value
   String hostname = config.getValue("mail.host", String.class);

   // dynamic with provider
   String resolvedHostName = hostName.get();

   ...
}
3.3.5.3.1. Best practices
  • Development:

    1. Create a file /src/main/resources-dev/local.properties (or *.yaml) with all properties which are expected. Reason: File has higher priority than the config server and is not part of the default build.

    2. Include folder /src/main/resources-dev/ only with development profile, so that it’s not part of the regular build artifact.

      With maven:

      <profiles>
        <profile>
          <id>dev</id>
          <build>
            <resources>
              <resource>
                <directory>src/main/resources-dev</directory>
              </resource>
            </resources>
          </build>
        </profile>
      </profiles>
3.3.5.3.2. Priority

ConfigSeeder Config Source has currently a priority of 200. If this should be configurable, create an issue on GitLab.

3.3.6. Spring

3.3.6.1. Requirements
3.3.6.2. Configuration

Add config-client-spring-*.jar as dependency:

pom.xml

  com.configseeder.client
  configseeder-client-spring
  2.25.4
3.3.6.3. Usage of the configuration values

Following examples show how you can inject a value at startup. Note that this value will never change anymore, although the may have been altered on the config server management instance.

StaticExample.java
// String value
@Value("${bar.key:my-default}")
String bar;

// Boolean value
@Value("${bar.a-boolean-value}")
boolean boolProp;

If you want to retrieve your values at runtime, just inject Environment and retrieve the value each time.

DynamicExample.java
@Autowired
Environment environment;

private void doSomeOperation() {
   // dynamic value with default
   String proxy = environment.getProperty("mail.proxy", "defaul-proxy.oneo.ch");

   // dynamic value with boolean datatype
   Boolean featureEnabled = environment.getProperty("mail.feature.enabled", Boolean.class);

   // ...
}
3.3.6.4. Best practices
  • Development:

    1. Create a file /src/main/resources-dev/application.properties (or *.yaml) with all properties which are expected. Reason: File has higher priority than the config server and is not part of the default build.

    2. Include folder /src/main/resources-dev/ only with development profile, so that it’s not part of the regular build artifact.

      With maven:

      <profiles>
        <profile>
          <id>dev</id>
          <build>
            <resources>
              <resource>
                <directory>src/main/resources-dev</directory>
              </resource>
            </resources>
          </build>
        </profile>
      </profiles>
3.3.6.5. Priority

Values from ConfigSeeder have always the least priority. Every local configuration will win.

3.3.7. Plain Java

3.3.7.1. Requirements
3.3.7.2. Configuration
  1. Add config-client-core-*.jar as dependency:

    pom.xml
    
      com.configseeder.client
      configseeder-client-core
      2.25.4
    
  2. If desired you can create a /application.properties file which could contain properties not available on the config server or have precendence over the config server.

3.3.7.3. Usage
  • Core ships a Config class, which can be used to easily retrieve values using default settings:

    SimpleConfigExample.java
    String stringValue = Config.getString("any.existing.string");
    String fallbackValue = Config.getString("any.notexisting.string", "fallback");
    Optional<String> optionalValue = Config.getOptionalString("any.optional.string");
    
    Long longValue = Config.getLong("any.existing.long");
    Long fallbackLongValue = Config.getLong("any.notexisting.long", 42L);
    Optional<Long> optionalLong = Config.getOptionalLong("any.optional.long");
    
    Boolean booleanValue = Config.getBoolean("any.existing.boolean");
    boolean fallbackBoolean = Config.getBoolean("any.notexisting.boolean", true);
    Optional<Boolean> optionalBoolean = Config.getBoolean("any.optional.boolean");
  • If you want to configure the ConfigSeederClient yourself or using a more dynamic way, use:

    CustomizedConfigSeederClient.java
    ConfigSeederClientConfiguration configSeederClientConfiguration = ConfigSeederClientConfiguration.fromYaml("/configseeder.yaml");
    ConfigSeederClient configSeederClient = new ConfigSeederClient(configSeederClientConfiguration);
    ...
    String stringValue = configSeederClient.getString("foo.bar");

    Make sure you create only one ConfigSeederClient instance.

3.3.8. Maven

3.3.8.1. Requirements
3.3.8.2. Configuration

Add following configuration to your pom.xml:

pom.xml

  
    
      src/main/resources
      
        filtered.properties
      
      true
    
  
  
    
      com.configseeder.client.maven
      configseeder-client-maven-plugin
      2.25.4
      
         fetch
      
      
        
          initialize
          
            fetch
          
        
      
      
        https://config-server-demo.oneo.ch
        ey...Ug0o
        demo
         PROD
         2.25.4
         
           
             cube
           
         
       
    
  
3.3.8.3. Usage
  • Use any property as replacement variable in your filtered files.

    1. Make sure files are filtered:

      pom.xml
      <resources>
        <resource>
          <directory>src/main/resources</directory>
          <includes>
            <include>filtered.properties</include>
          </includes>
          <filtering>true</filtering>
        </resource>
      </resources>
    2. Use the variables in your filtered files:

      filtered.properties
      foo.bar=@foo.bar@
  • Use any property in your pom.xml as variable:

    pom.xml
    <dependencies>
      <dependency>
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-plugin-api</artifactId>
        <version>${my.configseeder.maven-plugin-version}</version>
      </dependency>
    </dependency>

3.3.9. Template

ConfigSeeder offers the functionality to replace all variables in a file using the replacement mechanism. ConfigSeeder analyses the file and will try to resolve all variables. You can choose how a variable is built. For our Demo case we assume that a variable is built like this: ${variable.key}. ConfigSeeder will look for the value stored on key variable.key.

The same as for all other configurations, ConfigSeeder needs to know following parameters:

  • On which configuration groups the request shall be performed.

  • Restriction filters like environment, version, date or/and labels

  • API Key in order to authenticate.

  • The prefix and postfix of the variables (${ and }) are encoded into the url, see attributes variablePrefix and variablePostfix.

A full fledged request may look like this:

curl -X POST \
  --header 'Authorization: Bearer <apiKey>' \
  --header 'Content-Type: multipart/form-data' \
  --header 'Accept: application/octet-stream' \
  --form "configuration=@/tmp/request.json;type=application/json" \
  --form "template=@/tmp/original.sh" \
  --output /tmp/replaced.sh \
  'https://demo.configseeder.com/external/configurations/replace?variablePrefix=%24%7B&variablePostfix=%7D'

Where the files look like this:

request.json
{
  "configurations": [
    {
      "configurationGroupKey": "MyConfigurationGroup",
      "selectionMode": "LATEST"
    }
  ],
  "dateTime": "2018-12-11T08:24:59.535Z",
  "environmentKey": "DEV",
  "version": "2.0.0-RC1",
  "labels": [ "test" ]
}
original.sh
#!/bin/sh

VARIABLE="${variable.key}"

echo $VARIABLE;

This will result in a file like:

replaced.sh
#!/bin/sh

VARIABLE="Hello World"

echo $VARIABLE;

It is possible to optionally pass a file with additional variables as JSON:

variables.json
{
  "this.is.my.key": "this is a value",
  "another.key": "another value",
  "key1": "value1"
}

The curl command would look like this:

curl -X POST \
  --header 'Authorization: Bearer <apiKey>' \
  --header 'Content-Type: multipart/form-data' \
  --header 'Accept: application/octet-stream' \
  --form "configuration=@/tmp/request.json;type=application/json" \
  --form "template=@/tmp/original.sh" \
  --form "variables=@/tmp/variables.json;type=application/json" \
  --output /tmp/replaced.sh \
  'https://demo.configseeder.com/external/configurations/replace?variablePrefix=%24%7B&variablePostfix=%7D'

Appendix A: ConfigSeeder Management / ValueProvider

A.1. Configuration Options

This chapter lists all configurations available for customization. As the application is built on top of spring-boot, you can configure your application in many different ways (see also: Spring Boot Externalized Configuration).

We recommend following variants:

  1. Passing configuration as environment variables, note that then the variable names needs to be defined in Snake Case.

  2. Pass variables with -- prefix. Example for spring.datasource.url use --spring.datasource.url=jdbc:…​

  3. Pass variables with an externalized configuration --spring.config.location=./ or --spring.config.location=/home/config-server/config.properties (or extension yml)

A.1.1. AesKeyGenerator

Download ConfigSeeder Util from https://configseeder.com/download/#other. This util helps you to generate an AES128 or AES256 key. This key will be used to encrypt ConfigurationValues.

java -jar configseeder-utils-{project-version}.jar

And select option 1. Follow the instructions shown.

A.1.2. RsaKeyGenerator

Download ConfigSeeder Util from https://configseeder.com/download/#other. This util is helps you generate a private and public key which will be used to encrypt the API Key issued by the config server.

java -jar configseeder-utils-{project-version}.jar

And select option 2. Follow the instructions shown.

A.2. SSL Certificate

It is recommended to run ConfigSeeder Management and ConfigSeeder ValueProvider with a SSL certificate. This section describes how to create self-signed certificate or signed certificate and how to configure them. Please replace the password changeit with your desired password.

A.2.1. Self-signed certificate

Create a PKCS12 keystore with:

keytool -genkeypair -alias configseeder-local -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650

Define a password (e.g. changeit, at least 6 characters) and your hostname in the CN field:

What is your first and last name?
  [Unknown]: localhost
What is the name of your organizational unit?
  [Unknown]:
What is the name of your organization?
  [Unknown]:
What is the name of your City or Locality?
  [Unknown]:
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]:
Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
  [no]:  yes

Enter key password for <configseeder-local>
  (RETURN if same as keystore password):

This will generate you a file named keystore.p12 which can be used by ConfigSeeder Management and ConfigSeeder ValueProvider

A.2.2. Signed certificate

These steps assume that you have already a key and a signed certificate:

openssl pkcs12 -export -inkey keystore.key -in keystore.crt -out keystore.p12 -passsword pass:changeit -name configseeder

Note: If no alias was defined, use the number 1 as alias name in the configuration

A.2.3. Configuration

Now configure the application to use the certificate and password.

SSL configurations
server.port=8443 (1)
security.require-ssl=true (2)
server.ssl.key-store-type=PKCS12 (3)
server.ssl.key-store=/path/to/keystore.p12 (4)
server.ssl.key-store-password=notset (5)
server.ssl.key-alias=configseeder (6)
1 Port on which the application gets served
2 Tell security to require requests over HTTPS
3 The format used for the keystore. In our example, PKCS12
4 The path to the keystore containing the certificate (alternative: classpath:keystore.p12)
5 The password used to generate the certificate
6 The alias mapped to the certificate (in our example either configseeder or 1)

Appendix B: Configure Keycloak as Identity Provider

ConfigSeeder can be integrated in the existing security infrastructure. The first two sections of this chapter explain how to set up Keycloak so ConfigSeeder can be integrated with it via OpenID Connect or Saml2. The last section gives on overview about how to create a realm, set up groups and clients, …​

B.1. Security Integration with OpenID Connect

B.1.1. Setup a new client

  1. Clients > Create

    1. Client ID: configseeder

    2. Client Protocol: openid-connect

    3. Save

  2. Clients > configseeder > Settings

    1. Access Type: confidential (with this an additional Tab Credentials) will be shown after save

    2. Valid Redirect URIs: Configure here your URLs. Use Placeholder / at the end. Example: https://configseeder.domain.com/

  3. Client > configseeder > Credentials

    1. Here you can regenerate the secret if desired

  4. Clients > Mappers > Add Builtin ([x] mandatory, [o] optional)

    1. [x] family name, [x] given name, [x] profile, [x] locale, [x] full name, [x] picture, [o] nickname, [x] email, [x] username

    2. Add selected

B.1.2. Fetch needed values for configuring ConfigSeeder

  • authorization-uri, token-uri, user-info-uri and jwk-set-uri from: Realm Settings > Endpoints > OpenId Endpoint Configuration

  • client-id from: Clients > Client ID

  • client-secret from: Clients > {Client} > Credentials > Secret

Example Keycloak Configuration:

application.yml
spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            authorization-uri:        https://<keycloak-server>/realms/<client-id>/protocol/openid-connect/auth
            token-uri:                https://<keycloak-server>/realms/<client-id>/protocol/openid-connect/token
            user-info-uri:            https://<keycloak-server>/realms/<client-id>/protocol/openid-connect/userinfo
            jwk-set-uri:              https://<keycloak-server>/realms/<client-id>/protocol/openid-connect/certs
            user-name-attribute:      preferred_username
        registration:
          keycloak:
            client-id:                <client-id>
            client-secret:            <client-secret>
            authorization-grant-type: authorization_code
            redirect-uri:             {baseUrl}/login/oauth2/code/{registrationId}
            scope:                    openid,profile
            clientName:               Keycloak
configseeder:
  server:
    security:
      auth:
        oauth2:
          mapping:
            keycloak:
              firstname:                given_name
              lastname:                 family_name
              username:                 preferred_username
              avatar_url:               picture
              accessTokenRolePaths:     realm_access.roles

B.2. Security Integration with SAML

B.2.1. Setup a new client

  1. Clients > Create

    1. Client ID: https://<URI>/saml2/service-provider-metadata/<provider-name>

      <URI>: Dns (and optionally port) under which the ConfigSeeder can be reached

      <provider-name>: Name of the identity provider configured in ConfigSeeder. See description of <id> in the paragraph about configuring saml.

      When using OpenID Connect the Client ID can be chosen at will. This is not the case with Saml. The client (in this case ConfigSeeder) sets a value as issuer, the Client ID on server side has to match this value exactly.

      <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
      https://demo.configseeder.com/saml2/service-provider-metadata/keycloak-oneo
      </saml2:Issuer>

      This also means that if multiple instances of ConfigSeeder are set up with Saml

      • every instance has it’s own url and therefore

      • needs its own Saml Client configuration

    2. Client Protocol: saml

    3. Save

  2. Clients > <configseeder-client-created-in-first-step>

    1. Client Signature Required: Set to OFF

      The reason for the need to disable client signature verification in keycloak is a bug in a framework used by ConfigSeeder. This bug leads to an AuthnRequest signed in a not standardized way and should be fixed with spring security 5.3, see https://github.com/spring-projects/spring-security/issues/7711

    2. Valid Redirect URIs: Configure here your URL. Use Placeholder /* at the end.

      Example: https://configseeder.domain.com/*

      There can be only one redirect URI per client (also see comment about setting the Client ID). If more than one ConfigSeeder should be integrated, create one client per ConfigSeeder.

  3. Tab Mappers

    1. Add Builtin (X500 surname, X500 givenName, X500 email)

    2. Add selected

    3. Mapper for Groups

      1. Create

      2. Mapper Type: Group list

      3. Group Attribute Name: Role

      4. Save

    4. Mapper for Username

      1. Create

      2. Mapper Type: User Property

      3. Property: username

      4. Friendly Name: username

      5. Save

    5. Mapper for Locale (optional, if your users in keycloak have such an attribute)

      1. Create

      2. Mapper Type: User Attribute

      3. Property: locale

      4. Friendly Name: locale

      5. Save

  4. Optional Setup IDP Initiated SSO: Client > <configseeder-client-created-in-first-step>

    1. IDP Initiated SSO URL Name: Select the name, under which configseeder should be available for IDP Initiated SSO

      Keycloak will show you the complete URL as soon as a value is entered.

    2. Fine Grain SAML Endpoint Configuration > Assertion Consumer Service POST Binding URL: https://<URI>/login/saml2/sso/<provider-name>

      This URL is invoked when somebody accesses the defined IDP Initiated SSO URL and successfully logs in. Like the Client ID, the POST Binding URL is defined by ConfigSeeder.

  5. Optional: Encrypt Assertions: Client > <configseeder-client-created-in-first-step>

    1. Encrypt Assertions: Set to ON

    2. Save

    3. Open Tab SAML Keys

    4. Copy Private Key and Certificate to be uses by ConfigSeeder

    5. Here you can also reg

B.2.2. Fetch needed values for configuring ConfigSeeder

B.2.2.1. Metadata endpoint

Keycloak provides some Information on its metadata endpoint: https://<keycloak-URI>>/realms/<Realm>/protocol/saml/descriptor

<!--

  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
  ~ and other contributors as indicated by the @author tags.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~ http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.

-->
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Name="urn:keycloak">
  <EntityDescriptor entityID="https://<IDP-URI>/realms/<Realm>"> (1)
    <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
      <KeyDescriptor use="signing">
        <dsig:KeyInfo>
          <dsig:KeyName>xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</dsig:KeyName>
          <dsig:X509Data>
            <dsig:X509Certificate>
              MIICrzCCAZcCB.........................................................== (2)
            </dsig:X509Certificate>
          </dsig:X509Data>
        </dsig:KeyInfo>
      </KeyDescriptor>
      <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://<IDP-URI>/realms/<Realm>/protocol/saml"/>
      ...
      <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://<IDP-URI>/realms/<Realm>/protocol/saml"/> (3)
    </IDPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>

The values marked with callouts are required to configure saml for ConfigSeeder. All details regarding configuration parameters can be found at the paragraph about configuring saml.

1 The entityID is configured in ConfigSeeder with the Property spring.security.saml2.relyingparty.registration.<id>.identityprovider.entity-id
2 This certificate will be used by ConfigSeeder to verify that Saml Tokens were really issued by Keycloak:

Property spring.security.saml2.relyingparty.registration.<id>.identityprovider.verification.credentials.[0].certificate-location

-----BEGIN CERTIFICATE-----
MIICrzCCAZcCB.........................................................==
-----END CERTIFICATE-----
3 The SSO Url is configured in ConfigSeeder with the Property spring.security.saml2.relyingparty.registration.<id>.identityprovider.sso-url

B.3. Configuring Keycloak

B.3.1. Setup a new realm

This step is not required if you already have a realm.

  1. Add new realm

    1. Name: configseeder

    2. Create

  2. Realm Settings > General

    1. Display name: ConfigSeeder

  3. Realm Settings > Login

    1. Everything: Off (or according to your needs)

    2. Login with email: On

  4. Realm Settings > Email

    1. Configure a Mailbox E-Mail with a valid E-Mail Address. Example:

      1. Host: mail.domain.com

      2. Port: 587

      3. From Display Name: Keycloak

      4. From: keycloak@domain.com

      5. Enable StartTLS: ON

      6. Enable Authentication: ON

      7. Username: keycloak@domain.com

      8. Password: …​

B.3.1.1. Private Key and Certificate for signing the SAML Request

Keycloak provides the Private Key and Certificate with which the SAML Request should be signed.

  1. Open the Client

  2. Tab SAML Keys

  3. The Certificate goes to attribute spring.security.saml2.relyingparty.registration.<id>.signing.credentials.[0].certificate-location

    The certificate must have the following prefix and postfix:

    • Prefix: -----BEGIN CERTIFICATE-----

    • Postfix: -----END CERTIFICATE-----

    The certificate can be inlined by the additional prefix data: 

    Complete exmample:

    certificate-location: |
      data:-----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----
  4. The Private Key is configured with the attribute spring.security.saml2.relyingparty.registration.<id>.signing.credentials.[0].private-key-location

    Keycloak exports private keys as RSA keys. ConfigSeeder requires private keys to be in PKCS8 format. This can be achieved by the following steps:

    1. Create a file rsa.key with the following content:

      certificate-location: |
      -----BEGIN RSA PRIVATE KEY-----
      ...
      -----END RSA PRIVATE KEY-----
    2. Convert private key with openssl: cat rsa.key | openssl pkcs8 -topk8 -nocrypt

    3. Configure private key

    private-key-location: |
      data:-----BEGIN PRIVATE KEY-----
      ...
      ----END PRIVATE KEY-----

    === Setup Roles

    1. Roles > Add Role

      1. Add all functional roles (SUPERADMIN, TENANTADMIN, APPLICATIONMANAGER, DEVELOPER, READER)

      2. Add all data roles (depends on your desired configuration group access setup)

      3. Create your own roles you want at the end to assign to the user. Examples:

        1. cs

        2. cs_prod_r

        3. cs_all

        4. SUPERADMIN

        5. DEVELOPER

    === Setup Groups

    1. admin

      1. cs, cs_all, SUPERADMIN

    2. dev

      1. cs, cs_prod_r, DEVELOPER

    === Setup Users

    1. Add User

    2. Add "username", "firstname", "lastname", "email verified"

    3. Save

    4. Tab "Credentials"

    5. Add "new Password", "Password confirmation", Temporary: Off

    6. Reset Password

    7. Assign Groups

    = ConfigSeeder Management - Security

    == Function Role Privilege Assignment

    Table 29. Default Function Role Privilege Mapping (detailed)
    Function Role Assigned Privileges

    SUPERADMIN

    • TENANT_CREATE

    • TENANT_READ

    • TENANT_READ_ALL

    • TENANT_UPDATE

    • TENANT_DELETE

    • TENANT_FULL_STATISTICS

    • TENANT_STATISTICS

    • ENVIRONMENT_CREATE

    • ENVIRONMENT_READ

    • ENVIRONMENT_UPDATE

    • ENVIRONMENT_DELETE

    • API_KEY_CREATE

    • API_KEY_CREATE_CONNECTORS

    • API_KEY_READ

    • API_KEY_UPDATE

    • API_KEY_UPDATE_OWN

    • API_KEY_REVOKE

    • API_KEY_REVOKE_OWN

    • CONFIGURATION_GROUP_CREATE

    • CONFIGURATION_GROUP_READ

    • CONFIGURATION_GROUP_UPDATE

    • CONFIGURATION_GROUP_DELETE

    • VERSIONED_CONFIGURATION_GROUP_CLONE

    • VERSIONED_CONFIGURATION_GROUP_READ

    • VERSIONED_CONFIGURATION_GROUP_UPDATE

    • VERSIONED_CONFIGURATION_GROUP_DELETE

    • VERSIONED_CONFIGURATION_GROUP_RELEASE

    • VERSIONED_CONFIGURATION_GROUP_UNRELEASE

    • CONFIGURATION_ASSEMBLY_CREATE

    • CONFIGURATION_ASSEMBLY_READ

    • CONFIGURATION_ASSEMBLY_UPDATE

    • CONFIGURATION_ASSEMBLY_DELETE

    • CONFIGURATION_NODE_CREATE

    • CONFIGURATION_NODE_READ

    • CONFIGURATION_NODE_UPDATE

    • CONFIGURATION_NODE_UPDATE_KEY

    • CONFIGURATION_NODE_UPDATE_TYPE

    • CONFIGURATION_NODE_DELETE

    • CONFIGURATION_VALUE_CREATE

    • CONFIGURATION_VALUE_READ

    • CONFIGURATION_VALUE_UPDATE

    • CONFIGURATION_VALUE_DELETE

    • CONFIGURATION_VALUE_SYNCHRONIZE

    • BINARY_CREATE

    • LABEL_CREATE

    • LABEL_READ

    • LABEL_UPDATE

    • LABEL_DELETE

    • USER_READ

    • USER_CREATE

    • USER_UPDATE

    • USER_DELETE

    • USER_REACTIVATE

    • GROUP_READ

    • GROUP_CREATE

    • GROUP_UPDATE

    • GROUP_DELETE

    • FUNCTION_ROLE_READ

    • FUNCTION_ROLE_CREATE

    • FUNCTION_ROLE_UPDATE

    • FUNCTION_ROLE_DELETE

    • LICENSE_UPDATE

    • MIGRATION

    LICENSEMANAGER

    • TENANT_FULL_STATISTICS

    • TENANT_STATISTICS

    • LICENSE_UPDATE

    TENANTADMIN

    • TENANT_READ

    • TENANT_UPDATE

    • TENANT_STATISTICS

    • ENVIRONMENT_CREATE

    • ENVIRONMENT_READ

    • ENVIRONMENT_UPDATE

    • ENVIRONMENT_DELETE

    • API_KEY_CREATE_CONNECTORS

    • API_KEY_READ

    • API_KEY_UPDATE_OWN

    • API_KEY_REVOKE

    • CONFIGURATION_GROUP_CREATE

    • CONFIGURATION_GROUP_READ

    • CONFIGURATION_GROUP_UPDATE

    • CONFIGURATION_GROUP_DELETE

    • VERSIONED_CONFIGURATION_GROUP_READ

    • CONFIGURATION_ASSEMBLY_READ

    • CONFIGURATION_NODE_READ

    • CONFIGURATION_VALUE_READ

    • LABEL_CREATE

    • LABEL_READ

    • LABEL_UPDATE

    • LABEL_DELETE

    APPLICATIONMANAGER

    • TENANT_READ

    • ENVIRONMENT_READ

    • API_KEY_CREATE_CONNECTORS

    • API_KEY_READ

    • API_KEY_UPDATE_OWN

    • API_KEY_REVOKE

    • CONFIGURATION_GROUP_CREATE

    • CONFIGURATION_GROUP_READ

    • CONFIGURATION_GROUP_UPDATE

    • CONFIGURATION_GROUP_DELETE

    • VERSIONED_CONFIGURATION_GROUP_CLONE

    • VERSIONED_CONFIGURATION_GROUP_READ

    • VERSIONED_CONFIGURATION_GROUP_UPDATE

    • VERSIONED_CONFIGURATION_GROUP_DELETE

    • VERSIONED_CONFIGURATION_GROUP_RELEASE

    • VERSIONED_CONFIGURATION_GROUP_UNRELEASE

    • CONFIGURATION_ASSEMBLY_CREATE

    • CONFIGURATION_ASSEMBLY_READ

    • CONFIGURATION_ASSEMBLY_UPDATE

    • CONFIGURATION_ASSEMBLY_DELETE

    • CONFIGURATION_NODE_CREATE

    • CONFIGURATION_NODE_READ

    • CONFIGURATION_NODE_UPDATE

    • CONFIGURATION_NODE_UPDATE_KEY

    • CONFIGURATION_NODE_UPDATE_TYPE

    • CONFIGURATION_NODE_DELETE

    • CONFIGURATION_VALUE_CREATE

    • CONFIGURATION_VALUE_READ

    • CONFIGURATION_VALUE_UPDATE

    • CONFIGURATION_VALUE_DELETE

    • BINARY_CREATE

    • LABEL_READ

    DEVELOPER

    • TENANT_READ

    • ENVIRONMENT_READ

    • API_KEY_CREATE

    • API_KEY_READ

    • API_KEY_UPDATE_OWN

    • API_KEY_REVOKE_OWN

    • CONFIGURATION_GROUP_READ

    • VERSIONED_CONFIGURATION_GROUP_CLONE

    • VERSIONED_CONFIGURATION_GROUP_READ

    • VERSIONED_CONFIGURATION_GROUP_UPDATE

    • VERSIONED_CONFIGURATION_GROUP_DELETE

    • CONFIGURATION_ASSEMBLY_CREATE

    • CONFIGURATION_ASSEMBLY_READ

    • CONFIGURATION_ASSEMBLY_UPDATE

    • CONFIGURATION_ASSEMBLY_DELETE

    • CONFIGURATION_NODE_CREATE

    • CONFIGURATION_NODE_READ

    • CONFIGURATION_NODE_UPDATE

    • CONFIGURATION_NODE_UPDATE_KEY

    • CONFIGURATION_NODE_DELETE

    • CONFIGURATION_VALUE_CREATE

    • CONFIGURATION_VALUE_READ

    • CONFIGURATION_VALUE_UPDATE

    • CONFIGURATION_VALUE_DELETE

    • BINARY_CREATE

    • LABEL_READ

    READER

    • TENANT_READ

    • ENVIRONMENT_READ

    • CONFIGURATION_GROUP_READ

    • VERSIONED_CONFIGURATION_GROUP_READ

    • CONFIGURATION_ASSEMBLY_READ

    • CONFIGURATION_NODE_READ

    • CONFIGURATION_VALUE_READ

    • LABEL_READ

    OPERATOR

    • TENANT_READ

    • ENVIRONMENT_READ

    • CONFIGURATION_GROUP_READ

    • LABEL_READ

    == Examples of Data Based Roles

    Table 30. Tenant Data Role Examples
    Tenant Role Infix Tenant Secured Type Access Mode Allowed Privileges

    mytenant

    DERIVED

    READ

    cs, cs_all

    mytenant

    DERIVED

    WRITE

    cs, cs_all

    mytenant

    W_ROLE

    READ

    cs, cs_all

    mytenant

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_w

    mytenant

    RW_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    WRITE

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_r

    mytenant

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_w

    Table 31. Configuration Value Data Role Examples
    Ten. R.Infix Ten. Sec.Type Env. R.Infix Env. Sec.Type Conf.Group R.Infix Conf.Group Sec.Type Conf.Group Val.Sec.Type Secured AccessMode Allowed Privileges

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    null

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    DERIVED

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant, cs_mytenant_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_sec_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1, cs_mytenant_team1_all

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_sec_r

    mytenant

    RW_ROLE

    prod

    W_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod, cs_mytenant_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod, cs_mytenant_team1_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod, cs_mytenant_team1_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod, cs_mytenant_team1_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod, cs_mytenant_team1_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod, cs_mytenant_team1_prod_all

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    RW_ROLE

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    DERIVED

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_prod_all, cs_mytenant_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    W_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    RW_ROLE

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    false

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    DERIVED

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    W_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    RW_ROLE

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    READ

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_r

    mytenant

    RW_ROLE

    prod

    SEPARATE_RW_ROLES

    team1

    SEPARATE_RW_ROLES

    true

    SEPARATE_RW_ROLES

    WRITE

    cs_all, cs_mytenant_all, cs_mytenant_team1_all, cs_mytenant_team1_prod_all, cs_mytenant_team1_prod_sec_w