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
,
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.operatorfabric.mongodb.uri
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 |
Indicates where the Users service can be reached from the other services. |
|
operatorfabric.servicesUrls.businessconfig |
Indicates where the Business service can be reached from the other services. |
|
operatorfabric.servicesUrls.cardsPublication |
yes |
|
Cards publication service URL |
operatorfabric.servicesUrls.cardsConsultation |
|
yes |
Cards consultation service URL |
operatorfabric.servicesUrls.authToken |
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 |
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 |
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 |
operatorfabric.cards-publication.cardSendingLimitPeriod |
3600 |
For the Rate limiter, this defines the time period (in seconds) during which |
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 |
|
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.
|
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 : |
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 |
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 :
|
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 |
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 : |
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:
|
security.oauth2.flow.mode |
PASSWORD |
no |
authentication mode, available options:
|
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 |
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 |
5 |
no |
Interval between sound replays (see |
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 : |
|
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 |
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 theweb-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 # 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 |
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
|
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
toPASSWORD
orCODE
; -
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
toIMPLICIT
; -
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:
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.
-
This directory should only contain the files to be added to the keystore.
-
The files can be nested inside directories.
-
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.
-
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:
-
Open a bash shell in the container you want to check
docker exec -it deploy_businessconfig_1 bash
-
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
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.
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.
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.
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.
Any action (read, create/update or delete) on external devices configuration is restricted to users with th ADMIN role.