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:
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.
|
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
-
Download ConfigSeeder H2 Instance from https://configseeder.com/download/#cs-management
-
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) -
Open your Browser on http://localhost:8080/
-
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
-
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
andPROD
). The most relevant value is theKey
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. -
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.
-
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.
-
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.
After clicking on Save the generated API Key is shown. You should keep it at a save place.
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:
-
Directly connect your application to ConfigSeeder with native clients
-
Indirectly using a ConfigSeeder Connector.
-
Kubernetes Connector
-
OS Connector
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:
-
Decide which database you want to use (Supported: PostgreSQL, Microsoft SQL Server, Oracle)
-
Get your trial or full ConfigSeeder license (mail at license@configseeder.com, mention: company name, database, number of configuration groups, number of environments)
-
Setup Database (connection url, username, password) (empty database, user with DDL privilege)
-
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)
-
Define your hostname on which the application should be available
-
If desired prepare a SSL Certificate
-
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:
-
Create
application.yaml
file -
Prepare configurations for:
-
Setup Authentication Mechanism
-
Enable Profile:
spring.profiles.active=setup
-
-
From the startup log fetch the credential needed and replace
{credential}
in the link bellow. -
Create a temporary setup user with: http://host:port/setup/user/login?username=setup&email=your@email.com&firstname=Setup&lastname=Account&dataRoles=cs,cs_all&functionalRoles=SUPERADMIN&credential={credential}
-
Upload License (https://<host>:<port>/ui/license)
-
-
Stop ConfigSeeder Management
-
Cleanup configurations
-
Remove setup profile
spring.profiles.active=setup
-
Remove sensitive values from the configuration and pass it as environment variables
-
-
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:
Key | M. | Description |
---|---|---|
|
M |
JDBC URL to your database.
|
|
M |
Username |
|
M |
Password |
|
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:
|
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.
Key | M. | Description |
---|---|---|
|
Port on which ConfigSeeder should be running. For SSL we suggest port |
|
|
Set to |
|
|
Your keystore format. Our example uses |
|
|
Path to your keystore. E.g. |
|
|
Password to your keystore. In our example: |
|
|
If more than one certificate is defined, use the alias or number. In our example: |
3.1.2.5. Encryption Keys
ConfigSeeder Management has three encryption keys which needs to be configured. We strongly recommend setting your own values.
Key | M. | Description |
---|---|---|
|
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.
Key | M. | Description |
---|---|---|
|
M |
Private key als PKCS8 PEM file. Either the location needs to specified (e.g. |
|
M |
Public key als PKCS8 PEM file. Either the location needs to specified (e.g. |
|
O |
Allows keys with less bits than 2048. Defaults to |
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.
Key | M. | Description |
---|---|---|
|
M |
Location of the keystore (e.g. |
|
M |
Password of the keystore |
|
Type of the keystore. Defaults to |
|
|
M |
Name of the private key inside the keystore. |
|
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:
-
OIDC / OAuth2 (preferred)
-
SAML
-
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]+
).
Key | M. | Description |
---|---|---|
|
Set to |
|
|
Set to |
|
|
Java regular expression which can be used for filtering relevant roles. |
|
|
M |
Set to |
|
Attribute name for the username. Defaults to: |
|
|
Attribute name for the email. Defaults to: |
|
|
Attribute name for the first name. Defaults to: 'firstname'. Often: |
|
|
Attribute name for the last name. Defaults to: |
|
|
Attribute name if first name and last name are sent together. Defaults to |
|
|
Attribute name for the avatar. Should contain the URL or the base64 encoded image. Defaults to |
|
|
Attribute name for the language (only for initialization). Defaults to |
|
|
If the roles should be extracted from access token. Example: |
|
|
If the roles should be extracted from the user info endpoint. Example: |
|
|
Comma separated list of roles which should be assigned to all authenticated users. E.g. |
|
|
Map with additional request params which should be sent to the IDP. Example |
|
|
M |
Identification of the application provided by the Identity Provider |
|
M |
Secret of the application provided by the Identity Provider |
|
Defaults to: |
|
|
M |
URL where user will be redirected after authentication to the identity provider. Default value: |
|
M |
Additional scopes. E.g. |
|
M |
Which name will be used to show on the login screen |
|
M |
URI where user should be redirected to log in |
|
M |
URI where a the system can retrieve a token |
|
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 |
|
|
M for OIDC |
URI to verify the token signature |
|
M |
Attribute which should be used to fetch the username. Defaults to: |
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\-]+
).
Key | M. | Description |
---|---|---|
|
M |
Should be set to |
|
M |
Identifies the SAML registration configuration (and is als part of the URL) |
|
K |
Location of the keystore. Only mandatory if certificates or private keys are stored in a keystore. |
|
K |
Password of the keystore. Only mandatory if certificates or private keys are stored in a keystore. |
|
Type of the keystore (Default: |
|
|
M |
Identification on the IDP |
|
M |
SAML Single Sign On URL |
|
E |
Locations of the X.509 certificate (PEM file) used for verification of incoming SAML messages. (Start with |
|
K |
Alias of the certificate in the configured keystore. |
|
E |
Private key (PEM file) used for signing or decrypting. (Start with |
|
E |
Relying Party X509Certificate (PEM file) shared with the identity provider. (Start with |
|
K |
Alias of the private key in the configured keystore. |
|
Password of the private key (only supported for keys in the keystore) |
|
|
Attribute name containing the username. Defaults to |
|
|
Attribute name containing the firstname. Defaults to |
|
|
Attribute name containing the lastname. Defaults to |
|
|
Attribute name containing the email. Defaults to |
|
|
Attribute name containing the locale. Defaults to |
|
|
Attribute name containing the role. Defaults to |
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:
Key | M. | Description |
---|---|---|
|
Set to |
|
|
Default roles if the header does not delivery any role. Defaults to empty. |
|
|
Comma separated IPs, from where the server shall accept header authentication. Only active if at least one ip is entered. |
|
|
Which header contains the username. Defaults to: |
|
|
Header containing the e-mail. Defaults to |
|
|
Header containing the firstname. Defaults to |
|
|
Header containing the lastname. Defaults to |
|
|
Header containing the name. Defaults to |
|
|
Header containing the roles. Defaults to |
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.
Key | M. | Description |
---|---|---|
|
Notification |
SMTP Server Hostname |
|
Notification |
SMTP Server Port |
|
Notification |
SMTP Server Login (if needed) |
|
Notification |
SMTP Server Password (if needed) |
|
Notification |
SMTP Authentication. Values: |
|
Notification |
SMTP Enable StartTLS. Values: |
Key | M. | Description |
---|---|---|
|
Number of maximum days an API Key can be valid. Defaults to 2 years ( = 730 days) |
Key | M. | Description |
---|---|---|
|
Port on which the server listens (Defaults to |
|
|
URL to the logo on the left top corner |
|
|
Calendar: First day of week. |
|
|
Available languages in the UI splitted by comma (Defaults to all supported languages = |
|
|
Log level on client side. Defaults to |
|
|
Which client logs are shown on server side. Defaults to |
Key | M. | Description |
---|---|---|
|
M |
Url to ConfigSeeder which should be used for E-Mails. Typically just |
|
M |
Sender of expiration notification |
|
Cron expression to set up scheduling of expiration notifications job (Defaults to 2 A.M.). CronSequenceGenerator |
|
|
Number of days before expiration date to send notification (Defaults to |
|
|
List of email addresses where to send expiration notification separated by |
|
|
Flag to notify owner of the (not generated / manually created) api key. Defaults to |
|
|
Flag to notify owner of the (generated) api key. Defaults to |
|
|
Cron expression to set up scheduling of expiration notifications job (Defaults to 2:15 A.M.). CronSequenceGenerator |
|
|
Number of days before expiration date to send notification (Defaults to |
|
|
Flag to enable notifications for expiring certificates. Defaults to |
|
|
List of email addresses where to send expiration notification separated by |
Key | M. | Description |
---|---|---|
|
If qualifiers like |
|
|
Defaults to |
|
|
If action logs should be collected. Default: |
|
|
If action logs should log read operations. Default: |
|
|
If action logs should log write operations. Default: |
|
|
If action logs should be cleaned up. Default: |
|
|
At which time action logs should be cleaned up. Defaults to |
|
|
Cleanup action logs older than x days. Defaults to |
|
|
Cleanup logs in junks and stop after x milliseconds. Defaults to |
|
|
Cleanup maximal x entries in a chunk. Defaults to |
|
|
If action log should be persisted lazily. Defaults to |
|
|
Initial delay for the first persistence operation in milliseconds. Defaults to |
|
|
How often lazy persistence should be performed in milliseconds. Defaults to |
|
|
If a credential needs to be passed when running in setup mode and creating a setup user. Defaults to |
|
|
A default credential, if not a new credential shall be generated on each startup. Default: `` (empty ⇒ new one will be generated). |
Key | Description |
---|---|
|
Maximum upload file 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 |
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 |
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 |
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.
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:
Key | M. | Description |
---|---|---|
|
Prefix for all data access roles. Defaults to |
|
|
Separator character. Defaults to |
|
|
Suffix for all read-specific data roles. Defaults to |
|
|
Suffix for all write-specific data roles. Defaults to |
|
|
Infix for all secured marked configuration values. Defaults to |
|
|
Role suffix for having privileges for all sub entities. Defaults to |
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.
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.
Object | Security Config |
---|---|
Configuration Group |
|
Environment |
|
Configuration Value |
|
Name | Identity Provider Role | Function Role | Data Role |
---|---|---|---|
Admin |
Admin |
|
|
Devops |
User |
|
|
3.1.5.2. DEVOPS by Application
Scenario: Administrator Role and independent DEVOPS Team with full access.
Object | Security Config |
---|---|
Configuration Group |
|
Environment |
|
Configuration Value |
|
Name | Identity Provider Role | Function Role | Data Role |
---|---|---|---|
Admin |
Admin |
|
|
DevOpsTeam1 |
DevOpsTeam1 |
|
|
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
.
Object | Security Config |
---|---|
Configuration Group |
|
Environment |
All exl.
|
Configuration Value |
|
Name | Identity Provider Role | Function Role | Data Role |
---|---|---|---|
Admin |
Admin |
|
|
Leader Org1 |
Org1Lead |
|
|
Developer Org1 |
Org1Developer |
|
|
3.1.6. Example ConfigSeeder Management Configurations
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 |
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
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
orclient-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:
-
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.)
-
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:
-
Create
application.yaml
-
Prepare configurations for:
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.
Key | M. | Description |
---|---|---|
|
Port on which ConfigSeeder should be running. For SSL we suggest port |
|
|
Set to |
|
|
Your keystore format. Our example uses |
|
|
Path to your keystore. E.g. |
|
|
Password to your keystore. In our example: |
|
|
If more than one certificate is defined, use the alias or number. In our example: |
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.
Key | M. | Description |
---|---|---|
|
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.
Key | M. | Description |
---|---|---|
|
M |
Public key als PKCS8 PEM file. Either the location needs to specified (e.g. |
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).
Key | M. | Description |
---|---|---|
|
M |
|
|
M |
ConfigSeeder Management base url, e.g. http://localhost:8080/ |
|
M |
API Key from ConfigSeeder Management, which should be used for synchronisation |
|
Connect timeout. Defaults to 3sec |
|
|
Read timeout. Defaults to 10sec |
|
|
Read timeout. Defaults to 10sec |
|
|
Proxy hostname |
|
|
Proxy port |
|
|
Defaults to 1000 msec (in msec) |
|
|
Defaults to 5000 msec (in msec) |
|
|
Defaults to 15m (in Duration, e.g. |
|
|
Filter to include only specific tenants (tenant keys, separated by |
|
|
Filter to include only specific environments (environment keys, separated by |
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 |
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. |
-
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.yamlconfigseeder: 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.yamlconfigseeder: 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.
-
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 Clientsjava -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
withNEVER
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 .
|
Config | Environment Variable | Mand./Opt | Description |
---|---|---|---|
|
|
M |
URL of the config server |
|
|
M |
API Key |
|
|
O |
Key of the tenant |
|
|
M |
Key of the environment |
|
|
O |
Filter: Version of the application |
|
|
O |
Filter: Context (newly Labels, multiple Labels separated by comma) |
|
|
O |
Filter: Date Time (Default: |
|
|
(M) |
Name of the configuration group |
|
|
O |
If there are multiple versions: |
|
|
O |
Instead of |
|
|
(M) |
This key can be used instead of the |
|
|
O |
How all values should be initialized. Following options exists: - - - |
|
|
O |
When the values shall be refreshed: - - - - |
|
|
O |
How often values get refreshed (in milliseconds, default: |
|
|
O |
How many milliseconds should be waited before a new request is performed (default: |
|
|
O |
How many times connection retry should be performed (default: |
|
|
O |
How long the client shall wait for initial connection (default: |
|
|
O |
How long an answer from the server shall take max (default: |
|
|
O |
Disable configseeder client with |
|
|
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 |
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:
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.
// 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.
@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:
-
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. -
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
-
Download matching version of
config-client-spring-*.jar
from our maven repository: https://maven.configseeder.com/
3.3.6.2. Configuration
Add config-client-spring-*.jar
as dependency:
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.
// 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.
@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:
-
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. -
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
-
Download matching version of
config-client-core-*.jar
from our maven repository: https://maven.configseeder.com/
3.3.7.2. Configuration
-
Add
config-client-core-*.jar
as dependency:pom.xmlcom.configseeder.client configseeder-client-core 2.25.4 -
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.javaString 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.javaConfigSeederClientConfiguration 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
-
Download matching version of config-client-maven-plugin-*.jar from our maven repository: https://maven.configseeder.com/
3.3.8.2. Configuration
Add following configuration to your 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.
-
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>
-
Use the variables in your filtered files:
filtered.propertiesfoo.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/andlabels
-
API Key in order to authenticate.
-
The prefix and postfix of the variables (
${
and}
) are encoded into the url, see attributesvariablePrefix
andvariablePostfix
.
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:
{
"configurations": [
{
"configurationGroupKey": "MyConfigurationGroup",
"selectionMode": "LATEST"
}
],
"dateTime": "2018-12-11T08:24:59.535Z",
"environmentKey": "DEV",
"version": "2.0.0-RC1",
"labels": [ "test" ]
}
#!/bin/sh
VARIABLE="${variable.key}"
echo $VARIABLE;
This will result in a file like:
#!/bin/sh
VARIABLE="Hello World"
echo $VARIABLE;
It is possible to optionally pass a file with additional variables as 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:
-
Passing configuration as environment variables, note that then the variable names needs to be defined in Snake Case.
-
Pass variables with
--
prefix. Example forspring.datasource.url
use--spring.datasource.url=jdbc:…
-
Pass variables with an externalized configuration
--spring.config.location=./
or--spring.config.location=/home/config-server/config.properties
(or extensionyml
)
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.
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
-
Clients > Create
-
Client ID:
configseeder
-
Client Protocol:
openid-connect
-
Save
-
-
Clients > configseeder > Settings
-
Access Type:
confidential
(with this an additional TabCredentials
) will be shown after save -
Valid Redirect URIs: Configure here your URLs. Use Placeholder
/
at the end. Example:https://configseeder.domain.com/
-
-
Client > configseeder > Credentials
-
Here you can regenerate the secret if desired
-
-
Clients > Mappers >
Add Builtin
([x] mandatory, [o] optional)-
[x] family name, [x] given name, [x] profile, [x] locale, [x] full name, [x] picture, [o] nickname, [x] email, [x] username
-
Add selected
-
B.1.2. Fetch needed values for configuring ConfigSeeder
-
authorization-uri
,token-uri
,user-info-uri
andjwk-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:
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
-
Clients > Create
-
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
-
-
Client Protocol:
saml
-
Save
-
-
Clients > <configseeder-client-created-in-first-step>
-
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
-
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.
-
-
Tab Mappers
-
Add Builtin
(X500 surname, X500 givenName, X500 email) -
Add selected
-
Mapper for Groups
-
Create
-
Mapper Type:
Group list
-
Group Attribute Name:
Role
-
Save
-
-
Mapper for Username
-
Create
-
Mapper Type:
User Property
-
Property:
username
-
Friendly Name:
username
-
Save
-
-
Mapper for Locale (optional, if your users in keycloak have such an attribute)
-
Create
-
Mapper Type:
User Attribute
-
Property:
locale
-
Friendly Name:
locale
-
Save
-
-
-
Optional
Setup IDP Initiated SSO
: Client > <configseeder-client-created-in-first-step>-
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.
-
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.
-
-
Optional:
Encrypt Assertions
: Client > <configseeder-client-created-in-first-step>-
Encrypt Assertions: Set to
ON
-
Save
-
Open Tab SAML Keys
-
Copy Private Key and Certificate to be uses by ConfigSeeder
-
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 -----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.
-
Add new realm
-
Name:
configseeder
-
Create
-
-
Realm Settings > General
-
Display name:
ConfigSeeder
-
-
Realm Settings > Login
-
Everything:
Off
(or according to your needs) -
Login with email:
On
-
-
Realm Settings > Email
-
Configure a Mailbox E-Mail with a valid E-Mail Address. Example:
-
Host:
mail.domain.com
-
Port:
587
-
From Display Name:
Keycloak
-
From:
keycloak@domain.com
-
Enable StartTLS:
ON
-
Enable Authentication:
ON
-
Username:
keycloak@domain.com
-
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.
-
Open the Client
-
Tab
SAML Keys
-
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-----
-
-
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:
-
Create a file
rsa.key
with the following content:certificate-location: | -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----
-
Convert private key with openssl:
cat rsa.key | openssl pkcs8 -topk8 -nocrypt
-
Configure private key
private-key-location: | data:-----BEGIN PRIVATE KEY----- ... ----END PRIVATE KEY-----
=== Setup Roles
-
Roles >
Add Role
-
Add all functional roles (
SUPERADMIN
,TENANTADMIN
,APPLICATIONMANAGER
,DEVELOPER
,READER
) -
Add all data roles (depends on your desired configuration group access setup)
-
Create your own roles you want at the end to assign to the user. Examples:
-
cs
-
cs_prod_r
-
cs_all
-
SUPERADMIN
-
DEVELOPER
-
-
=== Setup Groups
-
admin
-
cs
,cs_all
,SUPERADMIN
-
-
dev
-
cs
,cs_prod_r
,DEVELOPER
-
=== Setup Users
-
Add User
-
Add "username", "firstname", "lastname", "email verified"
-
Save
-
Tab "Credentials"
-
Add "new Password", "Password confirmation", Temporary: Off
-
Reset Password
-
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
-