The aim of this document is to explain how to configure and deploy OperatorFabric.

1. Deployment

For now OperatorFabric consist of Docker images available either by compiling the project or by using images releases from Dockerhub

Service images are all based on openjdk:8-jre-alpine.

For simple one instance per service deployment, you can find a sample deployment as a docker compose file here

To run OperatorFabric in development mode, see the development environment documentation .

2. User computer

From the user perspective, OperatorFabric is compatible with recent versions of chrome, edge and firefox and has no specific hardware performance constraints.

Be aware that if you use OperatorFabric in realtime you should prevent user’s computers to go in "sleep mode".

In particular, if you are using edge have a look to github.com/opfab/operatorfabric-core/issues/2754

3. Configuration

The configuration is divided into two parts, the configuration of the business services and the configuration of the UI.

Opfab comes with a default embedded configuration, but certain parameters need to be provided and configured.

The configuration is centralized in the config directory of the GitHub repository. The dev subdirectory contains configurations specific to development environments, while the docker subdirectory contains a specific configuration meant for use in a full docker environment.

3.1. Business service configuration

3.1.1. Shared service configuration

The configuration shared by all services is in a yaml file, you can find an example with the file /config/docker/common.yml.

3.1.1.1. Mongo configuration

We only use URI configuration for mongo through the usage of the operatorfabric.mongodb.uri, it allows us to share the same configuration behavior for simple or cluster configuration and with both spring classic and reactive mongo configuration. See mongo connection string for the complete URI syntax.

This configuration is mandatory (no default configuration is provided).

3.1.2. RabbitMQ

A rabbitMQ component (docker image) is provided in opfab, it has a default configuration.

SECURITY WARNING : The default user/password should be changed if a rabbit port is exposed outside of docker network. To do that, set the environment variables "RABBITMQ_DEFAULT_USER" and "RABBITMQ_DEFAULT_PASS" when starting the image and adjust the following parameters in the common yml config file.

Name

Default

Mandatory

Description

operatorfabric.rabbitmq.host

rabbitmq

no

RabbitMQ host address

operatorfabric.rabbitmq.port

5672

no

RabbitMQ listen port

operatorfabric.rabbitmq.username

guest

no

RabbitMQ username

operatorfabric.rabbitmq.password

guest

no

RabbitMQ password

3.1.2.1. Internal account

The back service cards-reminder ,cards-diffusion and supervision need an internal account to communicate between opfab back services. Therefore, if you intend to utilize any of these services, it is necessary to create an Opfab technical account with ADMIN permissions and configure it

Name

Default

Mandatory

Description

operatorfabric.internalAccount.login

opfab

no

user account used to call Opfab services.

operatorfabric.internalAccount.password

yes

user password to call Opfab services

The services require knowledge of the URL to retrieve the account’s token, and this URL should be configured within operatorfabric.servicesUrls.authToken. A default value, based on OperatorFabric default installation, is set to: "http://web-ui/auth/token".

3.1.2.2. Optional configuration values
Name Default Description

operatorfabric.servicesUrls.users

users:2103

Indicates where the Users service can be reached from the other services.

operatorfabric.servicesUrls.businessconfig

businessconfig:2100

Indicates where the Business service can be reached from the other services.

operatorfabric.servicesUrls.cardsPublication

cards-publication:2102

yes

Cards publication service URL

operatorfabric.servicesUrls.cardsConsultation

cards-consultation:2104

yes

Cards consultation service URL

operatorfabric.servicesUrls.authToken

web-ui/auth/token

Authentication service URL

operatorfabric.userActionLogActivated

3.1.3. Service specific configurations

Each service can have a specific yaml configuration file that can override the default configuration

Examples of configuration of each service can be found under config/docker or config/dev .

3.1.3.1. Businessconfig service

The businessconfig service has this specific property :

Name Default Description

operatorfabric.businessconfig.storage.path

/businessconfig-storage

File path to data storage folder

3.1.3.2. Users service

The user service has these specific properties :

Name Default Description

operatorfabric.users.default.users

null

Array of user objects to create upon startup if they don’t exist

operatorfabric.users.default.user-settings

null

Array of user settings objects to create upon startup if they don’t exist

operatorfabric.users.default.groups

null

Array of group objects to create upon startup if they don’t exist

operatorfabric.users.default.entities

null

Array of entity objects to create upon startup if they don’t exist

operatorfabric.users.daysBeforeLogExpiration

61

Duration beyond which user action logs get automatically deleted

3.1.3.3. Cards-publication service

The cards publication service has these specific properties :

Name Default Description

operatorfabric.cards-publication.checkAuthenticationForCardSending

true

If false, OperatorFabric will not require user authentication to send or delete a card via endpoint /cards (it does not concern user cards which always need authentication). Be careful when setting the value to false, nginx conf must be adapted for security reasons (see security warning in the reference nginx.conf)

operatorfabric.cards-publication.authorizeToSendCardWithInvalidProcessState

false

If true, OperatorFabric will allow to publish a card referring a not existent process or state

operatorfabric.cards-publication.checkPerimeterForCardSending

true

If true, OperatorFabric will check user perimeter for card sending via endpoint /cards (it does not concern user cards which are always controlled).

spring.kafka.consumer.group-id

null

If set, support for receiving cards via Kafka is enabled

spring.deserializer.value.delegate.class

io.confluent.kafka.serializers. KafkaAvroDeserializer

Deserializer used to convert the received bytes into objects

spring.serializer.value.delegate.class

io.confluent.kafka.serializers. KafkaAvroSerializer

Serializer used to convert cards to bytes

spring.kafka.producer.bootstrap-servers

localhost:9092

comma separated list of URL(s) of the broker(s) / bootstrap server(s)

operatorfabric.cards-publication.kafka.topics.card.topicname

opfab

Name of the topic to read the messages from

operatorfabric.cards-publication.kafka.topics.response-card.topicname

opfab

Name of the topic to place the response cards to

operatorfabric.cards-publication.kafka.schema.registry.url

localhost:8081

URL of the schema registry. Can be set to the empty string "" is no registry is used

operatorfabric.cards-publication.delayForDeleteExpiredCardsScheduling

60000

The delay in millisecond after the last execution finished and the next execution starts.

operatorfabric.cards-publication.cardSendingLimitCardCount

1000

For the Rate limiter, this defines how many cards can be sent during cardSendingLimitPeriod.

operatorfabric.cards-publication.cardSendingLimitPeriod

3600

For the Rate limiter, this defines the time period (in seconds) during which cardSendingLimitCardCount is applied.

operatorfabric.cards-publication.activateCardSendingLimiter

true

If false, the Rate limiter will be ignored when sending cards.

OperatorFabric Kafka configuration

Next to publishing cards to OperatorFabric using the REST API, OperatorFabric also supports publishing cards via a Kafka Topic. In the default configuration Kafka is disabled. To enable Kafka you need to set the consumer group to the consumer group you assign to the OpFab Kafka consumer. This can be any group-id, as long as it isn’t used by other consumers (unless you explicitly want multiple consumers for the same group).

You can set the group_id by uncommenting the kafka.consumer.group_id in the cards-publication.yml

  kafka:
    consumer:
      group-id: opfab-command

By default, the consumer will consume messages from the opfab topic. See Spring for Apache Kafka for more information on the Spring Kafka implementation.

With the default settings, the Kafka consumer expects a broker running on http//127.0.0.1:9092 and a schema registry on 127.0.0.1:8081.

Operator Fabric is also able to publish response cards to a Kafka topic. The default topic name opfab-response. You can specify which response cards are to be returned via Kafka by setting the external-recipients in the cards-publication yaml file. Instead of setting http:// URL you should set it to kafka:

operatorfabric:
  cards-publication:
    external-recipients:
      recipients:
        - id: "processAction"
          url: "http://localhost:8090/test"
          propagateUserToken: true
        - id: "mykafka"
          url: "kafka:topicname"
          propagateUserToken: false

Note that topicname is a placeholder for now. All response cards are returned via the same Kafka response topic, as specified in the opfab.kafka.topics.response-card field.

Also note enabling Kafka does not disable the REST interface.

Example Kafka configuration plain:

spring:
  application:
    name: cards-publication
  deserializer:
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.consumer.KafkaAvroWithoutRegistryDeserializer
  serializer:
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.producer.KafkaAvroWithoutRegistrySerializer
  kafka:
    consumer:
      group-id: OPFAB
      properties:
        specific:
          avro:
            reader: true
    producer:
      client-id: operatorfabric-producer
    bootstrap-servers: kafka-server:9092
operatorfabric:
  cards-publication:
    kafka:
      topics:
        card:
          topicname: m_opfab-card-commands_dev
        response-card:
          topicname: m_opfab-card-response_dev

Example Kafka configuration SASL:

spring:
  application:
    name: cards-publication
  deserializer:
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.consumer.KafkaAvroWithoutRegistryDeserializer
  serializer:
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.producer.KafkaAvroWithoutRegistrySerializer
  kafka:
    consumer:
      group-id: OPFAB
      security:
        protocol: SASL_SSL
      properties:
        specific:
          avro:
            reader: true
        sasl:
          mechanism: SCRAM-SHA-256
          jaas:
            config: org.apache.kafka.common.security.scram.ScramLoginModule required username="kafkaUsername" password="kafkaPassword";
    producer:
      client-id: operatorfabric-producer
      security:
        protocol: SASL_SSL
      properties:
        sasl:
          mechanism: SCRAM-SHA-256
          jaas:
            config: org.apache.kafka.common.security.scram.ScramLoginModule required username="kafkaUsername" password="kafkaPassword";
    bootstrap-servers: kafka-server:9094
    ssl:
      trust-store-type: PKCS12
      trust-store-password: truststorePassword
      trust-store-location: file:///etc/truststore.pkcs
    properties:
      ssl:
        endpoint:
          identification:
            algorithm: ""
operatorfabric:
  cards-publication:
    kafka:
      topics:
        card:
          topicname: opfab-card-commands
        response-card:
          topicname: opfab-card-response

Example Kafka configuration Kerberos:

spring:
  application:
    name: cards-publication
  deserializer:
    key:
      delegate:
        class: org.apache.kafka.common.serialization.StringDeserializer
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.consumer.KafkaAvroWithoutRegistryDeserializer
  serializer:
    value:
      delegate:
        class: org.opfab.cards.publication.kafka.producer.KafkaAvroWithoutRegistrySerializer
  kafka:
    security:
      protocol: SASL_SSL
    properties:
      sasl.mechanism: GSSAPI
      sasl:
        jaas:
          config: com.sun.security.auth.module.Krb5LoginModule required useKeyTab=true keyTab="/etc/kafkaUsername.keytab" storeKey=true useTicketCache=false serviceName="kafka" principal="kafkaUsername@DOMAIN";
    bootstrap-servers: kafka-server:9094
    ssl:
      trust-store-type: pkcs12
      trust-store-password: truststorePassword
      trust-store-location: file:///etc/truststore.pkcs12
    consumer:
      group-id: OPFAB
      key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      properties:
        spring:
          deserializer:
            key:
              delegate:
                class: org.apache.kafka.common.serialization.StringDeserializer
            value:
              delegate:
                class: org.opfab.cards.publication.kafka.consumer.KafkaAvroWithoutRegistryDeserializer
    producer:
      client-id: OPFAB
      value-serializer: org.opfab.cards.publication.kafka.producer.KafkaAvroWithoutRegistrySerializer
operatorfabric:
  cards-publication:
    kafka:
      topics:
        card:
          topicname: opfab-card-commands
        response-card:
          topicname: opfab-card-response

Example Kafka configuration OAUTHBEARER / AADToken:

spring:
  kafka:
    consumer:
      properties:
        "[security.protocol]": SASL_SSL
        "[sasl.jaas.config]": org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required ;
        "[sasl.mechanism]": OAUTHBEARER
        "[sasl.login.callback.handler.class]": org.opfab.cards.publication.kafka.auth.AADWorkloadIdentityLoginCallbackHandler
    producer:
      ...the same...
3.1.3.4. Cards-consultation service

The cards-consultation service has these specific properties :

Name Default Description

operatorfabric.checkIfUserIsAlreadyConnected

true

If false, OperatorFabric will allow a user to have several sessions opened at the same time. However, it may cause synchronization problems between the sessions using the same login, so it is recommended to let it true, its default value.

operatorfabric.heartbeat.checkIntervalInSeconds

10

Frequency at which the heartbeat from the ui to the server is checked.

operatorfabric.heartbeat.delayInSecondsToConsiderUserDisconnected

100

After how many seconds without heartbeat, the user is considered disconnected

3.1.3.5. External devices service

The external devices service can be configured with the following properties:

Name Default Description

operatorfabric.externaldevices.watchdog.enabled

false

If true, watchdog signals will be sent to external devices to show that the OperatorFabric is running and connected.

operatorfabric.externaldevices.watchdog.cron

*/5 * * * * *

CRON expression determining when watchdog signals should be sent to external devices.

operatorfabric.externaldevices.watchdog.signalId

0

Id of the signal the external devices are expecting as watchdog

3.1.3.6. Cards external diffusion service

The card external diffusion service can be configured with the following properties:

Name

Default

Mandatory

Description

operatorfabric.logConfig.logFolder

logs

no

Log file folder (inside docker container)

operatorfabric.logConfig.logFile

"opfab.%DATE%.log"

no

Log file name

operatorfabric.logConfig.logLevel

info

no

Default log level

operatorfabric.mail.host

yes

Mail server host

operatorfabric.mail.port

yes

Mail server port

operatorfabric.mail.auth.user

yes

Mail server authentication user

operatorfabric.mail.auth.pass

yes

Mail server authentication password

operatorfabric.cardsExternalDiffusion.adminPort

2106

no

Listen port

operatorfabric.cardsExternalDiffusion.activeOnStartup

true

no

Flag to start the notification service on startup

operatorfabric.cardsExternalDiffusion.defaultConfig.mailFrom

yes

Mail from address

operatorfabric.cardsExternalDiffusion.defaultConfig.dailyEmailTitle

'Cards received during the day'

Daily mail subject prefix

operatorfabric.cardsExternalDiffusion.defaultConfig.hourToSendDailyEmail

7

Hour at which to send the daily recap email

operatorfabric.cardsExternalDiffusion.defaultConfig.minuteToSendDailyEmail

30

Minute at which to send the daily recap email

operatorfabric.cardsExternalDiffusion.defaultConfig.subjectPrefix

'Opfab card received'

no

Mail subject prefix

operatorfabric.cardsExternalDiffusion.defaultConfig.bodyPrefix

'You received a card in opfab : '

no

Mail body prefix

operatorfabric.cardsExternalDiffusion.defaultConfig.bodyPostfix

'This email has been sent by Opfab, there is no need to reply.'

no

Mail body postfix

operatorfabric.cardsExternalDiffusion.defaultConfig.publisherEntityPrefix

'The card has been sent by '

no

Publisher entity text prefix

operatorfabric.cardsExternalDiffusion.defaultConfig.opfabUrlInMailContent

yes

Url to write in mail to access the opfab server (example : opfab_url/)

operatorfabric.cardsExternalDiffusion.defaultConfig.windowInSecondsForCardSearch

360

no

Max time interval used to query new cards (in seconds)

operatorfabric.cardsExternalDiffusion.defaultConfig.secondsAfterPublicationToConsiderCardAsNotRead

60

no

Time period after card publication before sending mail notification

operatorfabric.cardsExternalDiffusion.defaultConfig.checkPeriodInSeconds

10

no

Time interval between consecutive checks

operatorfabric.cardsExternalDiffusion.defaultConfig.activateCardsDiffusionRateLimiter

true

no

Flag to activate mail sending rate limiting

operatorfabric.cardsExternalDiffusion.defaultConfig.sendRateLimit

100

no

Max number of mail sent allowed for a single destination in the configured period

operatorfabric.cardsExternalDiffusion.defaultConfig.sendRateLimitPeriodInSec

3600

no

Time period for rate limiting control

The parameters in "operatorfabric.cardsExternalDiffusion.defaultConfig" section can be modified at runtime by sending an http POST request to the /config API of cards external diffusion service with a JSON payload containing the config parameters to be changed.

3.1.3.7. Cards reminder service

The card reminder service can be configured with the following properties:

Name

Default

Mandatory

Description

operatorfabric.logConfig.logFolder

logs

no

Log file folder (inside docker container)

operatorfabric.logConfig.logFile

"opfab.%DATE%.log"

no

Log file name

operatorfabric.logConfig.logLevel

info

no

Default log level

operatorfabric.cardsReminder.adminPort

2107

no

Cards reminder service Listen port

operatorfabric.cardsReminder.activeOnStartup

yes

no

Flag to start the cards reminder service on startup

operatorfabric.cardsReminder.checkPeriodInSeconds

5

no

Cards reminder time interval between consecutive checks

3.1.3.8. Supervisor service

The supervisor service can be configured with the following properties:

Name

Default

Mandatory

Description

operatorfabric.logConfig.logFolder

logs

no

Log file folder (inside docker container)

operatorfabric.logConfig.logFile

"opfab.%DATE%.log"

no

Log file name

operatorfabric.logConfig.logLevel

info

no

Default log level

operatorfabric.supervisor.adminPort

2106

no

Cards reminder service Listen port

operatorfabric.supervisor.activeOnStartup

yes

no

Flag to start the cards reminder service on startup

operatorfabric.supervisor.defaultConfig.considerConnectedIfUserInGroups

no

If set the user must be in one of the mention groups to consider his entities as connected, example : ["Dispatcher","Planner"]

operatorfabric.supervisor.defaultConfig.entitiesToSupervise

no

List of entity id and related supervisor list ({ "id": "ENTITY1", "supervisors": ["ENTITY2"] })

operatorfabric.supervisor.defaultConfig.disconnectedCardTemplate.publisher

opfab

no

Publisher for card template used to send connections alerts

operatorfabric.supervisor.defaultConfig.disconnectedCardTemplate.process

supervisor

no

Process for card template used to send connections alerts

operatorfabric.supervisor.defaultConfig.disconnectedCardTemplate.processVersion

1

no

Process version for card template used to send connections alerts

operatorfabric.supervisor.defaultConfig.disconnectedCardTemplate.state

disconnectedEntity

no

State for card template used to send connections alerts

operatorfabric.supervisor.defaultConfig.disconnectedCardTemplate.severity

ALARM

no

Severity for card template used to send connections alerts

operatorfabric.supervisor.defaultConfig.secondsBetweenConnectionChecks

10

no

Connection check period

operatorfabric.supervisor.defaultConfig.nbOfConsecutiveNotConnectedToSendFirstCard

3

no

Number of consecutive failed checks before sending first alert card

operatorfabric.supervisor.defaultConfig.nbOfConsecutiveNotConnectedToSendSecondCard

12

no

Number of consecutive failed checks before sending second alert card

operatorfabric.supervisor.defaultConfig.processesToSupervise

no

List of processes and related states to monitor for missing acknowledgment ({ "process": "defaultProcess", "states": ["processState"] })

operatorfabric.supervisor.defaultConfig.unackCardTemplate.publisher

opfab

no

Publisher for card template used to send acknowledgment alerts

operatorfabric.supervisor.defaultConfig.unackCardTemplate.process

supervisor

no

Process for card template used to send acknowledgment alerts

operatorfabric.supervisor.defaultConfig.unackCardTemplate.processVersion

1

no

Process version for card template used to send acknowledgment alerts

operatorfabric.supervisor.defaultConfig.unackCardTemplate.state

unacknowledgedCards

no

State for card template used to send acknowledgment alerts

operatorfabric.supervisor.defaultConfig.unackCardTemplate.severity

ALARM

no

Severity for card template used to send acknowledgment alerts

operatorfabric.supervisor.defaultConfig.windowInSecondsForCardSearch

1200

no

Time period used to search for not acknowledged cards

operatorfabric.supervisor.defaultConfig.secondsBetweenAcknowledgmentChecks

10

no

Card acknowledgment check period

operatorfabric.supervisor.defaultConfig.secondsAfterPublicationToConsiderCardAsNotAcknowledged

600

no

Time period to wait after card publishing to consider a card as not acknowledged and send the alert

The parameters in "operatorfabric.supervisor.defaultConfig" section can be modified at runtime by sending an http POST request to the /config API of supervisor service with a JSON payload containing the config parameters to be changed.

3.2. Web UI Configuration

OperatorFabric Web UI service is built on top of a NGINX server. It serves the Angular SPA to browsers and act as a reverse proxy for other services.

3.2.1. NGINX configuration

An external nginx.conf file configures the OperatorFabric Nginx instance named web-ui service. Those files are mounted as docker volumes. There are two of them in OperatorFabric, one in config/dev and one in config/docker.

The one in config/dev is set with permissive CORS rules to enable web development using ng serve within the ui/main project.It’s possible to use ng serve with the one in config/docker version also. To do so use the conf file named nginx-cors-permissive.conf by configuring the /docker-compose.yml with the following line:

- "./nginx-cors-permissive.conf:/etc/nginx/conf.d/default.conf"

instead of:

- "./nginx.conf:/etc/nginx/conf.d/default.conf"

The line customized in the nginx configuration file must end with à semicolon (';') otherwise the Nginx server will stop immediately

3.2.2. UI properties

The properties lie in the web-ui.json.The following table describes their meaning and how to use them. An example file can be found in the config/docker directory.

name default mandatory? Description

about

none

no

Declares application names and their version into web-ui about section.
Each entry is a free key value followed by its name (a string of characters), its version (a string of characters) and its facultative rank of declaration (a number).
It is not necessary to declare OperatorFabric as application because it is added automatically with current release version and rank 0.
For example adding Keycloak application, with 'Keycloak' as name, 1 as rank and '6.0.1' as version should look like:

"about": {
    "keycloak": {
      "name": "Keycloak",
      "rank": 1,
      "version": "6.0.1"
    }
  }

alerts.hideBusinessMessages

false

no

if set to true, the business alert messages are hidden

alerts.messageBusinessAutoClose

false

no

if set to true, the (red) business alert message will automatically close after a few seconds

alerts.messageOnBottomOfTheScreen

false

no

if set to true, the alert message is shown on the bottom of the page

archive.filters.page.size

10

no

The page size of archive filters

archive.filters.publishDate.days

10

no

The default search period (days) for publish date filter in archives page

archive.filters.tags.list

no

List of tags to choose from in the corresponding filter in archives page

archive.history.size

100

no

The maximum size of card history visible

checkIfUrlIsLocked

true

no

if set to false, an OperatorFabric url can be used by several tabs in the same browser. Note that there can only be one token per browser for a given OperatorFabric url, so the first session will be replaced by the second one

defaultEntryPage

feed

no

This configuration determines the default page that will be displayed after a user logs in. The possible values include all core menus, with the exception of 'usercard' and 'about'. Additionally, you can set a custom application as the entry page by using 'businessconfigparty/menuId' as the value.

environmentColor

blue

no

Color of the background of the environment name. The format of color is css, for example : red , #4052FF

environmentName

no

Name of the environment to display in the top-right corner (examples: PROD , TEST .. ), if the value not set the environment name is not shown .

externalDevicesEnabled

false

no

If true, users have the opportunity to play sounds on external devices rather than in the browser. See settings.playSoundOnExternalDevice

feed.card.hideAckAllCardsFeature

true

no

Control if you want to show or hide the option for acknowledging all the visible cards of the feed

feed.card.hideApplyFiltersToTimeLineChoice

false

no

Control if you want to show or hide the option of applying filters or not to timeline in the feed page

feed.card.hideResponseFilter

false

no

Control if you want to show or hide the response filter in the feed page

feed.card.hideProcessFilter

false

no

Control if you want to show or hide the process filter in the feed page

feed.card.hideStateFilter

false

no

Control if you want to show or hide the state filter in the feed page

feed.card.maxNbOfCardsToDisplay

100

no

Max number of card visible in feed (This limit is used for performance reasons, setting the value too high can have consequences on browser response times)

feed.card.secondsBeforeLttdForClockDisplay

180

no

Number of seconds before lttd when a clock is activated in cards on the feed

feed.card.time.display

BUSINESS

no

card time display mode in the feed. Values :

  • BUSINESS: displays card with entire business period. It is the fallback if the set value is none of the values listed here;

  • BUSINESS_START: displays card with business start date;

  • PUBLICATION: displays card with publication date;

  • LTTD: displays card with lttd date;

  • NONE: nothing displayed.

feed.card.titleUpperCase

true

no

If set to true, the card titles are in UPPERCASE. (Option applies to the 'Card Feed', 'Archives' and 'Monitoring')

feed.defaultAcknowledgmentFilter

"notack"

no

Default card acknowledgment filtering option in feed page, possible values are : "notack", "ack", "all".

feed.defaultSorting

"unread"

no

Default card sorting option in feed page, possible values are : "unread", "date", "severity", "startDate", "endDate".

feed.enableGroupedCards

false

no

(Experimental feature): If true cards with the same tags are grouped together. Clicking on the grouped card will show the other cards with the same tags in the feed.

feed.geomap.bglayer.xyz.crossOrigin

null

no

Custom xyz/tiled background layer crossOrigin setting.

feed.geomap.bglayer.xyz.tileSize

null

no

Custom xyz/tiled background layer tile size (Int value, example: 256. Required when using custom background layer).

feed.geomap.bglayer.xyz.url

null

no

Custom xyz/tiled background layer service URL, Replaces OSM background layer (Add endpoint with {x}{y}{z} variables. Example: "https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/grijs/EPSG:3857/{z}/{x}/{y}.png".

feed.geomap.defaultDataProjection

EPSG:4326

no

The default data projection of card.wktGeometry to use when no wktProjection is embedded in the card.

feed.geomap.enable

false

no

(Experimental feature): If true a geographical map will be shown and cards that have geographical coordinates will be drawn on the map.

feed.geomap.enableGraph

false

no

Show a graph on the map with number of cards per severity.

feed.geomap.highlightPolygonStrokeWidth

2

no

Define the stroke width when highlighting polygon in the geomap view of a card.

feed.geomap.initialLatitude

0

no

The initial latitude of the map when no cards with geographical coordinates are present.

feed.geomap.initialLongitude

0

no

The initial longitude of the map when no cards with geographical coordinates are present.

feed.geomap.initialZoom

1

no

Initial zoom level of the map.

feed.geomap.layer.geojson

null

no

List of GeoJSON layers to add to the map with optional style attribute. The style object can have styling properties for stroke, fill, image, and text styles as defined in OpenLayer flat style (openlayers.org/en/latest/apidoc/module-ol_style_flat.html). Example: [{"url":"assets/layers/service-area.geojson"},{"url":"https://localhost:8000/network-lines.geojson", "style": {"stroke-color": "blue"}}]

feed.geomap.maxZoom

11

no

Max zoom level, to prevent zooming in too much when only one card is shown (or multiple cards in close proximity).

feed.geomap.popupContent

publishDateAndTitle

no

Define the content of the geomap popup. Possible values are : publishDateAndTitle (default value) or summary.

feed.geomap.zoomDuration

500

no

Time in milliseconds it takes to zoom the map to the specific location. Set to 0 to disable the zoom animation.

feed.geomap.zoomToLocation

14

no

Zoom level when zooming to a location of a selected card.

feed.showSearchFilter

false

no

If set to false, the search filter is hidden.

feed.timeline.domains

["TR", "J", "7D", "W", "M", "Y"]

no

List of domains to show on the timeline, possible domains are : "TR", "J", "7D", "W", "M", "Y".

heartbeatSendingInterval

30

yes

Frequency in seconds at which the ui sends heartbeat to the server

i18n.supported.locales

no

List of supported locales (Only fr and en so far) Values should be taken from the TZ database.

logging.filters.publishDate.days

10

no

The default search period (days) for publish date filter in logging page

logging.filters.tags.list

no

List of tags to choose from in the corresponding filter in logging page

logo.base64

medium OperatorFabric icon

no

The encoding result of converting the svg logo to Base64, use this online tool to encode your svg. If it is not set, a medium (32px) OperatorFabric icon is displayed.

logo.height

40

no

The height of the logo (in px) (only taken into account if logo.base64 is set). The value cannot be more than 48px (if it is set to more than 48px, it will be ignored and set to 48px).

logo.width

40

no

The width of the logo (in px) (only taken into account if logo.base64 is set).

secondsToCloseSession

60

no

Number of seconds between logout and token expiration. If you use IMPLICIT authentication mode, exercise caution when modifying the value to prevent logouts before token silent refresh.

security.changePasswordUrl

no

URL to change the user password (if the top-right menu item "Change password" is visible)

security.logout-url

yes

The keycloak logout URL. Is a composition of: - Your keycloak instance and the auth keyword (ex: www.keycloakurl.com/auth), but we also support domains without auth (ex: www.keycloakurl.com/customPath) - The realm name (Ex: dev) - The redirect URL (redirect_uri): The redirect URL after success authentication

security.oauth2.flow.delegate-url

null

no

Url to redirect the browser to for authentication. Mandatory with:

  • CODE flow: must be the url with protocol choice and version as query parameters;

  • IMPLICIT flow: must be the url part before .well-known/openid-configuration (for example in dev configuration it’s localhost:89/auth/realms/dev).

security.oauth2.flow.mode

PASSWORD

no

authentication mode, available options:

  • CODE: Authorization Code Flow;

  • PASSWORD: Direct Password Flow (fallback);

  • IMPLICIT: Implicit Flow.

security.implicit-mode-post-logout-url

null

no

The redirect URL after logout when using IMPLICIT flow

selectActivityAreaOnLogin

false

no

if set to true the users belonging to multiple Entities will be required to configure activity area on login

settings.dateFormat

Value from the browser configuration

no

Format for date rendering (example : DD/MM/YYYY )

settings.dateTimeFormat

Value from the browser configuration

no

Format for date and time rendering (example : HH:mm DD/MM/YYYY )

settings.locale

en

no

Default user locale (use en if not set)

settings.playSoundForAction

false

no

If set to true, a sound is played when Action cards are added or updated in the feed

settings.playSoundForAlarm

false

no

If set to true, a sound is played when Alarm cards are added or updated in the feed

settings.playSoundForCompliant

false

no

If set to true, a sound is played when Compliant cards are added or updated in the feed

settings.playSoundForInformation

false

no

If set to true, a sound is played when Information cards are added or updated in the feed

settings.playSoundOnExternalDevice

false

no

If set to true (and externalDevicesEnabled is set to true as well) and the user has an external device configured, sounds will be played on this device rather than in the browser

settings.remoteLoggingEnabled

false

no

If set to true, some logs from the UI are sent to the back and write in the log file of the cards-consultation service

settings.replayEnabled

false

no

If set to true, sounds are replayed every settings.replayInterval seconds until the user interacts with the application

settings.replayInterval

5

no

Interval between sound replays (see settings.replayEnabled)

settings.systemNotificationAction

false

no

If set to true, a system notification is sent when Action cards are added or updated in the feed

settings.systemNotificationAlarm

false

no

If set to true, a system notification is sent when Alarm cards are added or updated in the feed

settings.systemNotificationCompliant

false

no

If set to true, a system notification is sent when Compliant cards are added or updated in the feed

settings.systemNotificationInformation

false

no

If set to true, a system notification is sent when Information cards are added or updated in the feed

settings.styleWhenNightDayModeDesactivated

no

Style to apply if not using day night mode, possible value are DAY or NIGHT

settings.timeFormat

Value from the browser configuration

no

Format for time rendering (example : HH:mm )

settingsScreen.hiddenSettings

no

Array of string indicating which field(s) we want to hide in the settings screen. Possible values :
"language" : if present, language field will not be displayed
"remoteLoggingEnabled" : if present, the checkbox to activate remote logging will not be displayed
"sounds" : if present, the checkboxes for sound notifications won’t be displayed
"systemNotifications" : if present, the checkboxes for systemNotifications won’t be displayed
"sendCardsByEmail" : if present, the email options won’t be displayed
"emailToPlainText" : if present, the email option to have the emails sent as plain text won’t be displayed "sendDailyEmail" : if present, the email option to have the daily email recap won’t be displayed

showUserEntitiesOnTopRightOfTheScreen

false

no

If set to true, the user’s entities with the "ACTIVITY_AREA" role will be displayed under the login on the top right of the screen

title

OperatorFabric

no

Title of the application, displayed on the browser

usercard.useDescriptionFieldForEntityList

false

no

If true, show entity description field instead of name in user card page

customJsToLoad

no

List of URLs of javascript files to be loaded at startup

IMPORTANT:

To declare settings parameters, you now need to group all fields under settings: { } For example:

Replace the following invalid settings config

  "settings.replayInterval": 10,
  "settings.replayEnabled": true,
  "settings": {
    "about": {
      "keycloak": {
        "name": "Keycloak",
        "rank": 2,
        "version": "6.0.1"
      },
    }
    "locale": "en",
    "dateTimeFormat": "HH:mm DD/MM/YYYY",
    "dateFormat": "DD/MM/YYYY",
    "styleWhenNightDayModeDesactivated": "NIGHT"
  },

By this valid one :

  "settings": {
    "replayInterval": 10,
    "replayEnabled": true,
    "about": {
      "keycloak": {
        "name": "Keycloak",
        "rank": 2,
        "version": "6.0.1"
      },
    }
    "locale": "en",
    "dateTimeFormat": "HH:mm DD/MM/YYYY",
    "dateFormat": "DD/MM/YYYY",
    "styleWhenNightDayModeDesactivated": "NIGHT"
  },

3.3. Security Configuration

Configure the security concern throughout several files:

  • nginx.conf of the nginx server

  • config/docker/common.yml the common configuration file for back services

  • web-ui.json served by the web-ui service;

3.3.1. Authentication configuration

There are 3 OAuth2 Authentication flows available into OperatorFabric UI:

  • password grant: referred as PASSWORD mode flow;

  • code flow : referred as CODE mode flow;

  • implicit flow: referred as IMPLICIT mode flow.

Alternatively there is also another flow available:

  • none flow: referred as NONE mode flow.

The NONE flow assumes that the application is behind a secured proxy wich handles login and token generation. Calls to backend services will get a valid token added to the headers and the token will not be visible for the (web)client.

3.3.1.1. Nginx Configuration

The UI calls need some mapping to reach the Authentication Provider. In the default OperatorFabric configuration it’s a docker keycloak instance, called keycloak in the project docker-compose.yml files.

There are 3 properties to configure within nginx.conf file:

  • $KeycloakBaseUrl: the base url of keycloak;

  • $OperatorFabricRealm: the realm configure within keycloak instance to provide authentication to OperatorFabric;

  • $ClientPairOFAuthentication: base64 encoded string of the pair of client authentication used by OperatorFabric to log to the Authentication Provider (keycloak). The cient-id and the client-secret are separated by a colon(':').

Example of the docker configuration

# Url of the Authentication provider
    set $KeycloakBaseUrl "http://keycloak:89";
# Realm associated to OperatorFabric within the Authentication provider
    set $OperatorFabricRealm "dev";

# base64 encoded pair of authentication in the form of 'client-id:secret-id'
    set $ClientPairOFAuthentication "b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==" ;

where b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA== is the base64 encoded string of opfab-client:opfab-keycloak-secret with opfab-client as client-id and opfab-keycloak-secret its client secret.

3.3.1.2. Configuration file common.yml

name

default

mandatory?

Description

operatorfabric.security.oauth2.resourceserver.jwt.jwk-set-uri

null

yes

The url providing the certificate used to verify the jwt signature

operatorfabric.security.jwt.loginClaim

sub

no

Jwt claim used for user login

example of common.yml

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri:  http://localhost:89/auth/realms/dev/protocol/openid-connect/certs
3.3.1.3. Configuration file web-ui.json

Nginx web server serves this file. OperatorFabric creates and uses a custom Docker image containing an Nginx server with a docker volume containing this file. The docker-compose.yml contains an example of it. The path in the image to it is /usr/share/nginx/html/opfab/web-ui.json.

For OAuth2 security concerns into this file, there are two ways to configure it, based on the Oauth2 chosen flow. There are several common properties:

  • security.logout-url: url used when a user is logged out of the UI;

  • security.oauth2.flow.delegate-url: url used to connect to the Authentication provider;

  • security.oauth2.flow.mode: technical way to be authenticated by the Autentication provider.

  • security.oauth2.client-id : the OAuth2 client ID

  • security.jwt.login-claim : the field in the token that will be used to identify your account (default value : sub)

3.3.1.4. OAuth2 PASSWORD or CODE Flows

These two modes share the same way of declaring the delegate URL. This has to be defined in web-ui.json by setting :

  • security.oauth2.flow.mode to PASSWORD or CODE;

  • security.oauth2.flow.delegate-url with the URL of the OAuth2 leading to the protocol used for authentication.

Example of Configuration For CODE Flow
{
    "security": {
      "jwt": {
        "login-claim": "preferred_username"
      },
      "oauth2": {
        "client-id": "OAUTHCLIENTID",
        "flow": {
          "mode": "CODE",
          "delegate-url": "http://localhost:89/auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client"
        }
      },
      "logout-url":"http://localhost:89/auth/realms/dev/protocol/openid-connect/logout?redirect_uri=http://localhost:2002/"
  }
}

Within the delegate-url property dev is the keycloak client realm of OperatorFabric. For keycloak instance used for development purposes, this delegate-url correspond to the realm under which the client opfab-client is registred. Here, the client-id value is opfab-client which is define as client under the realm named dev on the dev keycloak instance.

3.3.1.5. OAuth2 IMPLICIT Flow

It had its own way of configuration. To enable IMPLICIT Flow authentication the following properties need to be set in web-ui.json:

  • security.oauth2.flow.mode to IMPLICIT;

  • security.oauth2.flow.delegate-url with the URL of the OAuth2 leading to the .well-known/openid-configuration end-point used for authentication configuration.

It is possible to configure the URL to redirect the browser after logout by setting the security.implicit-mode-post-logout-url parameter in web-ui.json. Be careful that the configured value should be one of the valid post logout redirect URIs configured on Keycloak realm.

Example of configuration for IMPLICIT Flow
{
  "security": {
    "jwt": {
        "login-claim": "preferred_username"
      },
    "oauth2": {
      "client-id": "OAUTHCLIENTID",
      "flow": {
        "mode": "IMPLICIT",
        "delegate-url": "http://localhost:89/auth/realms/dev"
      }
    },
    "logout-url":"http://localhost:89/auth/realms/dev/protocol/openid-connect/logout?redirect_uri=http://localhost:2002/",
    "implicit-mode-post-logout-url": "https://opfab.github.io"
  }
}

Within the delegate-url property dev is the keycloak client realm of OperatorFabric. For keycloak instance used for development purposes, this delegate-url correspond to the realm under which the client opfab-client is registred. The url look up by the implicit ui mechanism is localhost:89/auth/realms/dev/.well-known/openid-configuration.

3.3.1.6. NONE Flow

The configuration for the NONE flow is a bit different because the token isn’t handled/visible in the front-end.

Nginx

The following variables can be removed:

  • $KeycloakBaseUrl

  • $OperatorFabricRealm

  • $ClientPairOFAuthentication

The locations for handling tokens can be edited to return a 401 by default. If one of these locations is called, the token generated by the secured proxy has expired.

location /auth/check_token {
  return 401;
}
location /auth/token {
  return 401;
}
location /auth/code/ {
  return 401;
}
Web-ui.json

Set the security.oauth2.flow.mode to NONE;

Set the security.oauth2.client-id to Your Oauth client ID;

Use the security.jwt.login-claim to select value from the token will be used to identify your account. In this example preferred_username is used;

Settings that are not required are:

  • delegate-url

Example of configuration
{
    "security": {
      "jwt": {
        "login-claim": "preferred_username"
      },
      "oauth2": {
        "client-id": "OAUTHCLIENTID",
        "flow": {
          "mode": "NONE"
        }
      },
      "logout-url":"http://my-secured-proxy/OAUTHTENANTID/oauth2/logout?client_id=OAUTHCLIENTID&post_logout_redirect_uri=https%3A%2F%2Flocalhost:2002%2Fui%2Fui/"
    }
 }

3.3.2. User creation

If a user exists in the authentication system but not in Opfab, their profile will be automatically created upon their initial login. The user’s name and family name will be extracted from the authentication token.

name default mandatory? Description

operatorfabric.security.jwt.givenNameClaim

given-name

no

Jwt claim to use to get the user’s name

operatorfabric.security.jwt.familyName-claim

family-name

no

Jwt claim to used to get the user’s family name

3.3.3. Alternative way to manage groups (and/or entities)

By default, OperatorFabric manages groups (and/or entities) through the user's collection in the database. Another mode can be defined, the JWT mode. The groups (and/or entities) come from the authentication token.

To activate this option , you need to set some parameter in the common.yml file :

name default mandatory? Description

operatorfabric.security.jwt.groups.mode

OPERATOR_FABRIC

no

Set the group mode, possible values JWT or OPERATOR_FABRIC

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandard.path

no

path in the JWT to retrieve the claim that defines a group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardArray.path

no

path in the JWT to retrieve the claim that defines an array of groups

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardList.path

no

path in the JWT to retrieve the claim that defines a list of group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardList.separator

no

set the separator value of the list of group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimCheckExistPath.path

no

path in the JWT to check if that path does exist, if it does, use the roleValue as a group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimCheckExistPath.roleValue

no

set the value of the group if the path exists

operatorfabric.security.jwt.entitiesIdClaim

no

set the name of the field in the token

operatorfabric.security.jwt.gettingEntitiesFromToken

no

boolean indicating if you want the entities of the user to come from the token and not mongoDB (possible values : true/false)

application.yml

operatorfabric:
  security:
    jwt:
      entitiesIdClaim: entitiesId
      gettingEntitiesFromToken: true
      groups:
        mode: JWT # value possible JWT | OPERATOR_FABRIC
        rolesClaim:
          rolesClaimStandard:
            - path: "ATTR1"
            - path: "ATTR2"            
          rolesClaimStandardArray:  
            - path: "resource_access/opfab-client/roles"
          rolesClaimStandardList:  
            - path: "roleFieldList" 
              separator: ";"           
          rolesClaimCheckExistPath: 
            - path: "resource_access/AAA" 
              roleValue: "roleAAA"      
            - path: "resource_access/BBB"
              roleValue: "roleBBB"  

JWT example

{
  "jti": "5ff87583-10bd-4946-8753-9d58171c8b7f",
  "exp": 1572979628,
  "nbf": 0,
  "iat": 1572961628,
  "iss": "http://localhost:89/auth/realms/dev",
  "aud": [
    "AAA",
    "BBB",
    "account"
  ],
  "sub": "example_user",
  "typ": "Bearer",
  "azp": "opfab-client",
  "auth_time": 0,
  "session_state": "960cbec4-fcb2-47f2-a155-975832e61300",
  "acr": "1",
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "AAA": {
      "roles": [
        "role_AAA"
      ]
    },
    "BBB": {
      "roles": [
        "role_BBB"
      ]
    },
    "opfab-client": {
      "roles": [
        "USER"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid ATTR2 email ATTR1 profile roleFieldList",
  "email_verified": false,
  "name": "example_firtstname example_lastname",
  "ATTR2": "roleATTR2",
  "ATTR1": "roleATTR1",
  "preferred_username": "example_user",
  "given_name": "example_firtstname",
  "entitiesId": "ENTITY1",
  "family_name": "example_lastname",
  "email": "example_user@mail.com",
  "roleFieldList": "roleA;roleB;roleC"
}

As the result, the group will be [ATTR1, ATTR2, roleA, roleB, roleC, USER, roleBBB, roleAAA]

3.3.4. Adding certification authorities or certificates to the Java keystore

If you’re using certificates (for example for Keycloak) that are not from a certification authority trusted by the JVM, this will cause errors such as this one:

Missing certificate error message
Caused by: sun.security.validator. ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to
requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:262)
at sun.security.ssl.X509TrustManager Impl.validate(x509TrustManagerImpl.java:330)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(x509TrustManagerImpl.java:237)
at sun.security.ssl.X509TrustManager Impl.checkServerTrusted(x509TrustManager Impl.java:132)
at sun.security.ssl.clientHandshaker.serverCertificate(ClientHandshaker.java:1621)
94 common frames omitted
Caused by: sun.security.provider.certpath. SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath. SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 100 common frames omitted

If that is the case, you can pass the additional authorities or certificates that you use to the containers at runtime.

To do so, put the relevant files (*.der files for example) under src/main/docker/certificates.

  1. This directory should only contain the files to be added to the keystore.

  2. The files can be nested inside directories.

  3. Each certificate will be added with its filename as alias. For example, the certificate in file mycertificate.der will be added under alias mycertificate. As a consequence, filenames should be unique or it will cause an error.

  4. If you need to add or remove certificates while the container is already running, the container will have to be restarted for the changes to be taken into account.

If you would like certificates to be sourced from a different location, replace the volumes declarations in the deploy docker-compose.yml file with the selected location:

volumes:
 - "path/to/my/selected/location:/certificates_to_add"

instead of

volumes:
 - "../../../../src/main/docker/certificates:/certificates_to_add"
The steps described here assume you’re running OperatorFabric in docker mode using the deploy docker-compose, but they can be adapted for single container deployments and development mode.

If you want to check that the certificates were correctly added, you can do so with the following steps:

  1. Open a bash shell in the container you want to check

    docker exec -it deploy_businessconfig_1 bash
  2. Run the following command

    $JAVA_HOME/bin/keytool -list -v -keystore /tmp/cacerts -storepass changeit

You can also look at the default list of authorities and certificates trusted by the JVM with this command:

$JAVA_HOME/bin/keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

3.3.5. IP address control

For each user it is possible to configure a list of authorized source ip addresses by setting the authorizedIPAddresses field in User object.

4. OperatorFabric CLI (Command Line Interface)

The OperatorFabric CLI is designed to streamline administrative tasks. It provides a user-friendly alternative to direct API access and custom scripting, enhancing efficiency and ease of use.

The CLI is automatically installed when you execute the bin/load_environment_light.sh script.

To use the CLI, simply type opfab help in your terminal to view a list of available commands.

Before you can start using the CLI, you must first authenticate yourself with the desired OperatorFabric instance using the login command.

For those preferring not to install npm and nodejs locally, the CLI is accessible via a Docker container. Utilize the cli/opfabDockerCli.sh script to launch a shell environment pre-configured with the opfab CLI. The script mounts your current directory to /host within the Docker container.

Using the Docker version, you can pre-set environment configurations through a config file. This file should list each environment configuration on a separate line. For example:

dev DEV LOCALHOST_URL 2002 admin test
prod PROD PROD_URL 80

Each line consists of: - The first value is the configuration name. - The second value is the name to be added to the shell prompt. - The third value is the URL of the OperatorFabric instance. - The fourth value is the port number to use. - The fifth value is the username. - The sixth value is the password (if not provided, it will be requested via a prompt).

To launch the Docker CLI using the config file, execute opfabDockerCli.sh with the config file as an argument.

5. Monitoring

Operator Fabric provides end points for monitoring via prometheus. The monitoring is available for the five following services: user, businessconfig, cards-consultation, cards-publication and external-devices. To enable the monitoring , you need to open the endpoint by adding in the common yml configuration file :

management:
  endpoints:
    web:
      exposure:
        include: '*'

You can start a test prometheus instance via config/monitoring/startPrometheus.sh , the monitoring will be accessible on localhost:9090/

It is also possible to monitor the health status of cards-external-diffusion, cards-reminder and supervisor services by making an HTTP GET request to the "/healthcheck" endpoint exposed by each service. The healthcheck endpoint will return a HTTP 200 status code if the service is up and running.

6. Logging Administration

6.1. Logging configuration

Log levels can be defined either globally in the YAML configuration file or individually for each service. You will find a commented configuration for each service within the directory config/docker.

Services are designed to write logs to two primary destinations: standard output and log files within the Docker containers.

If you want to save logs you can redirect the standard output to a file.

Or you can also map the Docker directory /var/log/opfab to a location on the host machine for each business services, keeping logs outside of the container.

For the web-ui service’s logs are in the /var/log/nginx directory. You can map this directory to the host to preserve those logs.

6.2. Runtime configuration

Operator Fabric includes the ability to view and configure the log levels at runtime through APIs for most of the business services. It is possible to configure and view an individual logger configuration, which is made up of both the explicitly configured logging level as well as the effective logging level given to it by the logging framework.

Querying and setting logging levels is restricted to administrators.

6.2.1. Java services

For java services (user, businessconfig, cards-consultation, cards-publication and external-devices) the available logging levels are:

  • TRACE

  • DEBUG

  • INFO

  • WARN

  • ERROR

  • FATAL

To view the configured logging level for a given logger it is possible to send a GET request to the '/actuator/logger' URI as follows:

curl http://<server>:<port>/actuator/loggers/${logger} -H "Authorization: Bearer ${token}" -H "Content-type:application/json"

where ${token} is a valid OAuth2 JWT for a user with administration privileges and ${logger} is the logger (ex: org.opfab)

The response will be a json object like the following:

{
  "configuredLevel" : "INFO",
  "effectiveLevel" : "INFO"
}

To get the log level for ROOT logger of a service it is possible to call the provided script src/test/resources/logs/getLogs.sh as follows: getLogs.sh <server:port> <user> <opfab_url>. For example to get the current log level for "card-publication" service execute: getLogs.sh localhost:2102 admin localhost

To configure a given logger, POST a json entity to the '/actuator/logger' URI, as follows:

curl -i -X POST http://<server>:<port>/actuator/loggers/${logger} -H "Authorization: Bearer ${token}" -H 'Content-Type: application/json' -d '{"configuredLevel": "DEBUG"}'

To “reset” the specific level of the logger (and use the default configuration instead) it is possible to pass a value of null as the configuredLevel.

To set the log level for ROOT logger of a service it is possible to call the provided script src/test/resources/logs/setLogs.sh as follows: setLogs.sh <server:port> <log_level> <user> <opfab_url>. For example to set the log level to DEBUG for "card-publication" service execute: setLogs.sh localhost:2102 DEBUG admin localhost

6.2.2. Node Services

For node services (supervisor, cards-external-diffusion and cards-reminder) the available logging levels are:

  • silly

  • debug

  • verbose

  • http

  • info

  • warn

  • error

To view the configured logging level for a given logger it is possible to send a GET request to the '/logLevel' URI as follows:

curl http://<server>:<port>/logLevel -H "Authorization: Bearer ${token}"

where ${token} is a valid OAuth2 JWT for a user with administration privileges

The response will be the configured logging level as string

To get the log level of a node service it is possible to call the provided script src/test/resources/logs/getLogsNodeService.sh as follows: getLogsNodeService.sh <server:port> <user> <opfab_url>. For example to get the current log level for "card-external-diffusion" service execute: getLogsNodeService.sh localhost:2106 admin localhost

To configure a given logger, POST a json entity to the '/logLevel' URI, as follows:

curl -i -X POST http://<server>:<port>/logLevel -H "Authorization: Bearer ${token}" -H 'Content-Type: application/json' -d '{"configuredLevel": "debug"}'

To “reset” the specific level of the logger (and use the default configuration instead) it is possible to pass a value of null as the configuredLevel.

To set the log level for a node service it is possible to call the provided script src/test/resources/logs/setLogsNodeService.sh as follows: setLogsNodeService.sh <server:port> <log_level> <user> <opfab_url>. For example to set the log level to DEBUG for "card-external-diffusion" service execute: setLogsNodeService.sh localhost:2106 DEBUG admin localhost

7. Users, Groups and Entities Administration

A new operator call John Doe, who has OAuth granted right to connect to current OperatorFabric instance, need to receive cards within current OperatorFabric instance. As a user of OperatorFabric, he needs to be added to the system with a login (john-doe-operator), his firstName (John) and his lastName (Doe). As there is no Administration GUI for the moment, it must be performed through command line, as detailed in the Users API.

7.1. Users

7.1.1. List all users

First of all, list the users (who are the recipients in OperatorFabric) of the system with the following commands:

Httpie

http http://localhost:2103/users "Authorization:Bearer $token" "Content-Type:application/type"

cURL

curl -v http://localhost:2103/users -H "Authorization:Bearer $token" -H "Content-Type:application/type"

response

HTTP/1.1 200 OK

[
    {
        "firstName": null,
        "groups": [
            "ADMIN"
        ],
        "entities": [
            "ENTITY1_FR",
            "ENTITY2_FR"
        ],
        "lastName": null,
        "login": "admin"
    },
    {
        "firstName": null,
        "groups": [
            "RTE",
            "ADMIN",
            "CORESO",
            "ReadOnly",
            "TEST"
        ],
        "lastName": null,
        "login": "operator3_fr"
    },
    {
        "firstName": null,
        "groups": [
            "ELIA"
        ],
        "lastName": null,
        "login": "elia-operator"
    },
    {
        "firstName": null,
        "groups": [
            "CORESO"
        ],
        "lastName": null,
        "login": "coreso-operator"
    },
    {
        "firstName": null,
        "groups": [
            "Dispatcher",
            "ReadOnly",
            "TEST"
        ],
        "entities": [
            "ENTITY1_FR"
        ],
        "lastName": null,
        "login": "operator1_fr"
    },
    {
        "firstName": null,
        "groups": [
            "Planner",
            "ReadOnly"
        ],
        "entities": [
            "ENTITY2_FR"
        ],
        "lastName": null,
        "login": "operator2_fr"
    },
]

7.1.2. Create a new User

We are sure that no John-doe-operator exists in our OperatorFabric instance. We can add him in our OperatorFabric instance using the following command use httpie:

echo '{"login":"john-doe-operator","firstName":"Jahne","lastName":"Doe"}' | http POST http://localhost:2103/users "Authorization:Bearer $token" "Content-Type:application/json"

Or here cURL:

curl -X POST http://localhost:2103/users -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"login":"john-doe-operator","firstName":"Jahne","lastName":"Doe"}'

response

HTTP/1.1 200 OK

{
    "firstName": "Jahne",
    "lastName": "Doe",
    "login": "john-doe-operator"
}

7.1.3. Fetch user details

It’s always a good thing to verify if all the information has been correctly recorded in the system:

with httpie:

http -b http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

or with cURL:

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "Jahne",
    "groups": [],
    "entities": [],
    "lastName": "Doe",
    "login": "john-doe-operator"
}

7.1.4. Update user details

As shown by this result, the firstName of the new operator has been misspelled. We need to update the existing user with john-doe-operator login. To correct this mistake, the following commands can be used:

with httpie:

echo '{"login":"john-doe-operator","lastName":"Doe","firstName":"John"}' | http PUT http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

or with cURL:

curl -X PUT http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"login":"john-doe-operator","firstName":"John","lastName":"Doe"}'

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "lastName": "Doe",
    "login": "john-doe-operator"
}

7.1.5. Notification configuration

Using the Opfab CLI it is possible to configure process/state notification for all users at once. Execute opfab users help command for usage details.

7.2. Groups/Entities

All the commands below :

  • List groups

  • Create a new group

  • Fetch details of a given group

  • Update details of a group

  • Add a user to a group

  • Remove a user from a group

are available for both groups and entities. In order not to overload the documentation, we will only detail group endpoints.

7.2.1. List groups (or entities)

This operator is the first member of a new group operator called the OPERATORS, which doesn’t exist for the moment in the system. As shown when we list the groups existing in the server.

Httpie

http http://localhost:2103/groups "Authorization:Bearer $token" "Content-Type:application/type"

cURL

curl http://localhost:2103/groups -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

[
    {
        "description": "The admin group",
        "name": "ADMIN group",
        "id": "ADMIN"
    },
    {
        "description": "RTE TSO Group",
        "name": "RTE group",
        "id": "RTE"
    },
    {
        "description": "ELIA TSO group",
        "name": "ELIA group",
        "id": "ELIA"
    },
    {
        "description": "CORESO Group",
        "name": "CORESO group",
        "id": "CORESO"
    },
    {
        "description": "Dispatcher Group",
        "name": "Dispatcher",
        "id": "Dispatcher"
    },
    {
        "description": "Planner Group",
        "name": "Planner",
        "id": "Planner"
    },
    {
        "description": "ReadOnly Group",
        "name": "ReadOnly",
        "id": "ReadOnly"
    }
]

7.2.2. Create a new group (or entity)

Firstly, the group called OPERATORS has to be added to the system using the following command:

using httpie:

echo '{"id":"OPERATORS","decription":"This is the brand new group of operator"}' | http POST http://localhost:2103/groups "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl -X POST http://localhost:2103/groups -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"id":"OPERATORS","decription":"This is the brand new group of operator"}'

response

HTTP/1.1 200 OK

{
    "perimeters": [],
    "description": null,
    "name": null,
    "id": "OPERATORS"
}

7.2.3. Fetch details of a given group (or entity)

The result returned seems strange, to verify if it’s the correct answer by displaying the details of the group called OPERATORS, use the following command:

using httpie:

http http://localhost:2103/groups/OPERATORS "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl http://localhost:2103/groups/OPERATORS -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "perimeters": [],
    "description": null,
    "name": null,
    "id": "OPERATORS"
}

7.2.4. Update details of a group (or entity)

The description is really null. After verification, in our first command used to create the group, the attribute for the description is misspelled. Using the following command to update the group with the correct spelling, the new group of operator gets a proper description:

with httpie:

echo '{"id":"OPERATORS","description":"This is the brand-new group of operator"}' | http -b PUT http://localhost:2103/groups/OPERATORS "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl -X PUT http://localhost:2103/groups/OPERATORS -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"id":"OPERATORS","description":"This is the brand-new group of operator"}'

response

{
    "perimeters": []
    "description": "This is the brand-new group of operator",
    "name": null,
    "id": "OPERATORS"
}

7.2.5. Add a user to a group (or entity)

As both new group and new user are correct it’s time to make the user a member of the group . To achieve this, use the following command:

with httpie:

echo '["john-doe-operator"]' | http PATCH http://localhost:2103/groups/OPERATORS/users "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl -X PATCH http://localhost:2103/groups/OPERATORS/users -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '["john-doe-operator"]'

response

HTTP/1.1 200 OK

Let’s verify that the changes are correctly recorded by fetching the :

http http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

with cURL

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "groups": ["OPERATORS"],
    "entities": [],
    "lastName": "Doe",
    "login": "john-doe-operator"
}

It’s now possible to send cards either specifically to john-doe-operator or more generally to the OPERATORS group.

7.2.6. Remove a user from a group (or entity)

When John Doe is no longer in charge of hypervising cards for OPERATORS group, this group has to be removed from his login by using the following command:

with httpie:

http DELETE http://localhost:2103/groups/OPERATORS/users/john-doe-operator "Authorization:Bearer $token"

with cURL:

curl -X DELETE -H "Authorization:Bearer $token" http://localhost:2103/groups/OPERATORS/users/john-doe-operator

response

HTTP/1.1 200 OK

{
	"login":"john-doe-operator","
	firstName":"John",
	"lastName":"Doe",
	"groups":[],
    "entities":[]
}

A last command to verify that OPERATORS is no longer linked to john-doe-operator:

with httpie:

http http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "groups": [],
    "entities": [],
    "lastName": "Doe",
    "login": "john-doe-operator"

}

7.2.7. Entity parents

Entities have a 'parents' attribute instead of a 'perimeters' one. This attribute is a string array. Each element of the array is the id of another Entity. When adding or patching an Entity into the system, operatorFabric performs a cycle detection. On a positive cycle detection cancels the addition or the patch.

7.2.7.1. Add a new Entity without a cycle in the parent declaration

using httpie:

echo '{"id":"NEW_ENTITY","name":"Last New Entity","description":"This is the last new entity","parents": ["ENTITY1_FR"]}' \
| http POST http://localhost:2103/entities "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl http://localhost:2103/entities -H "Authorization:Bearer $token" -H "Content-Type:application/json" \
--data '{"id":"NEW_ENTITY","name":"Last New Entity","description":"This is the last new entity","parents": ["ENTITY1_FR"]}'

response

HTTP/1.1 200 OK

{
    "id": "NEW_ENTITY",
    "parents": ["ENTITY1_FR"],
    "name": "Last New Entity",
    "description":"This is the last new entity",
    "labels":[]
}
7.2.7.2. Add a new Entity with a cycle

For simplicity, in this example the new entity will declare itself as a parent. This auto-referencing triggers a cycle detection.

using httpie:

echo '{"id":"NEW_ENTITY","name":"Last New Entity","description":"This is the last new entity","parents": ["NEW_ENTITY"]}' | http POST http://localhost:2103/entities "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl http://localhost:2103/entities -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"id":"NEW_ENTITY","name":"Last New Entity","description":"This is the last new entity","parents": ["NEW_ENTITY"]}'

response

{
    "status":"BAD_REQUEST",
    "message":"A cycle has been detected: NEW_ENTITY->NEW_ENTITY"
}

with a 400 as http status return.

8. Service port table

Port Service Description

89

KeyCloak

KeyCloak api port

2002

web-ui

Web ui and gateway (Nginx server)

2100

businessconfig

Businessconfig management service http (REST)

2102

cards-publication

Cards publication service http (REST)

2103

users

Users management service http (REST)

2104

cards-consultation

Cards consultation service http (REST)

2105

external-devices

External devices management service http (REST)

2106

cards-external-diffusion

Mail notification service http (REST)

2107

cards-reminder

Cards reminder service http (REST)

2108

supervisor

Supervisor service http (REST)

27017

mongo

Mongo api port

5672

rabbitmq

Amqp api port

15672

rabbitmq

Rabbitmq api port

1025

MailHog

MailHog SMTP port (for testing only)

8025

MailHog

MailHog web UI and API http (REST) (for testing only)

9. Rate limiter for card sending

External publishers are monitored by this module which limits how many cards they can send cardSendingLimitCardCount in a period of time cardSendingLimitPeriod . This is to avoid potential overloading due to external apps stuck in a card sending loop. It can be disabled / enabled with activateCardSendingLimiter . It is also possible to reset the data of the limiter by calling the API through the "/cards/rateLimiter" endpoint (e.g. POST "$url:2102/cards/rateLimiter" -H "Authorization:Bearer $token").

10. Restricted operations (administration)

Some operations are restricted to users with administrative roles, either because they are administration operations with the potential to impact the OperatorFabric instance as a whole, or because they give access to information that should be private to a user.

Below is a quick recap of these restricted operations.

Users Service

Any action (read, create/update or delete) regarding a single user’s data (their personal info such as their first and last name, as well as their settings) can be performed either by the user in question or by a user with the ADMIN role.

Any action on users, groups, entities or perimeters lists (if authorization is managed in OperatorFabric) is restricted to users with the ADMIN role.

Access to users action logs information is restricted to users with ADMIN role or VIEW_USER_ACTION_LOGS role.

Businessconfig Service

Any write (create, update or delete) action on bundles can only be performed by a user with the ADMIN role or ADMIN_BUSINESS_PROCESS role. As such, administrators are responsible for the quality and security of the provided bundles. In particular, as it is possible to use scripts in templates, they should perform a security check to make sure that there is no XSS risk.

Cards consultation Service

Users holding the ADMIN role are authorized to read all archived cards, regardless of the card’s sender, recipients, or the user’s assigned perimeters IMPORTANT: The ADMIN role doesn’t grant any special privileges when it comes to current cards consultation so a user with the ADMIN role will only receive cards that have been addressed to them (or to one of their groups or entities), just like any other user.

External devices service

Any action (read, create/update or delete) on external devices configuration is restricted to users with th ADMIN role.