-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1723 from folio-org/FOLIO-873-improve-docs-280
FOLIO-873 improve docs
- Loading branch information
Showing
5 changed files
with
310 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
--- | ||
layout: null | ||
--- | ||
|
||
# mod-settings | ||
|
||
Copyright (C) 2022-2023 The Open Library Foundation | ||
|
||
This software is distributed under the terms of the Apache License, | ||
Version 2.0. See the file "[LICENSE](LICENSE)" for more information. | ||
|
||
## Introduction | ||
|
||
mod-settings is a key-value store that provides a more secure | ||
configuration system than the old mod-configuration. | ||
|
||
See [Fixing the security problem in mod-configuration](https://github.com/MikeTaylor/folio-docs/blob/main/doc/fixing-mod-configuration.md) | ||
for more information. | ||
|
||
It is currently implemented with PostgresQL as storage. | ||
|
||
A setting consists of these required properties | ||
|
||
* `id`: unique identifier (UUID) | ||
* `scope`: it is a namespace for the setting. This could be module | ||
* `key`: a readable identifier; not necessarily for display | ||
* `value`: any type (integer, string, object, array) | ||
|
||
And optionally: | ||
|
||
* `userId`: the owner of the setting | ||
|
||
We call settings without userId 'global'. Global settings must be unique | ||
for scope and key. Non-global settings must be unique for scope, key | ||
and userId. | ||
|
||
Settings are protected by permissions. | ||
|
||
In order to write to a global setting, the client must have permission | ||
`mod-settings.global.write.`scope, where scope is used in the setting. | ||
To read this type of setting, the client must have | ||
`mod-settings.global.read.`scope. | ||
|
||
In order to write to a setting with any userId, which we call a user | ||
setting, the client must have permission `mod-settings.users.write.`scope , | ||
where scope is used in the setting. To read this type of setting, | ||
the client must have `mod-settings.users.read.`scope. | ||
|
||
In order to write to user's own setting, the client must have permission | ||
`mod-settings.owner.write.`scope, where scope is used in the setting. | ||
To read this type of setting, the client must have | ||
`mod-settings.owner.read.`scope. | ||
|
||
With 'read' in this context we mean able to read the content of | ||
the setting. With 'write' in this context we modify storage | ||
using POST, PUT and DELETE. | ||
|
||
We expect that most users will have read-write permission on their own | ||
settings, and read-only permission on global entries. | ||
|
||
The API is CRUD-like, but with some important changes for some. | ||
|
||
Create a setting [with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/postSetting): | ||
|
||
POST /settings/entries | ||
|
||
This is write operation and returns 204 if successful. The | ||
`id` property is required and must be supplied by the client. | ||
|
||
Fetch a particular setting [with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/getSetting): | ||
|
||
GET /settings/entries/{id} | ||
|
||
This fetch is protected by permissions. If the client does not | ||
have permission to read the setting with the scope or if the setting | ||
does not exist, then mod-settings will return a 404 failure. It is a | ||
deliberate choice to not distinguish between these two cases. | ||
|
||
Get a list of settings [with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/getSettings): | ||
|
||
GET /settings/entries | ||
|
||
The latter takes optional `query`, `limit` (default 10), `offset` (default 0) parameters. | ||
Query is expressed in Contextual Query Language | ||
([CQL](https://dev.folio.org/reference/glossary/#cql)) | ||
and supports queries on the `id`, `scope`, | ||
`key` and `userId` fields. Query terms can mostly only be used in | ||
exact-value matching: the exception is that the key field supports | ||
right-truncated searches, e.g. `scope=foo and key=bar*` to find all | ||
entries in the `foo` scope that begin with `bar`. | ||
|
||
The GET operations are "read" operations. The entries returned | ||
are limited by client permissions. | ||
|
||
Update a setting | ||
[with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/putSetting): | ||
|
||
PUT /settings/entries/{id} | ||
|
||
This returns 204 if the setting was updated. This is strictly "write", i.e. | ||
does not return the newly modified setting. | ||
|
||
Delete a setting | ||
[with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/deleteSetting): | ||
|
||
DELETE /settings/entries/{id} | ||
|
||
This is a write operation and returns 204 if the setting was deleted. | ||
|
||
It's also possible to upload (or "import") settings | ||
[with](https://s3.amazonaws.com/foliodocs/api/mod-settings/settings.html#operation/uploadSettings): | ||
|
||
PUT /settings/upload | ||
|
||
Settings are created/updated with this service. The provided settings must | ||
not include an identifier. An identifier will be assigned by the server when | ||
necessary. | ||
|
||
## Compilation | ||
|
||
Requirements: | ||
|
||
* Java 17 or later | ||
* Maven 3.6.3 or later | ||
* Docker (unless `-DskipTests` is used) | ||
|
||
Note: Debian package maven-3.6.3-1 | ||
[does not work with Java16/Java17](https://bugs.launchpad.net/ubuntu/+source/maven/+bug/1930541) | ||
|
||
|
||
You need `JAVA_HOME` set, e.g.: | ||
|
||
* Linux: `export JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:bin/javac::")` | ||
* macOS: `export JAVA_HOME=$(/usr/libexec/java_home -v 17)` | ||
|
||
Build all components with: `mvn install` | ||
|
||
## Server | ||
|
||
You will need Postgres 12 or later. | ||
|
||
You can create an empty database and a user with, e.g: | ||
|
||
``` | ||
CREATE DATABASE folio_modules; | ||
CREATE USER folio WITH CREATEROLE PASSWORD 'folio'; | ||
GRANT ALL PRIVILEGES ON DATABASE folio_modules TO folio; | ||
``` | ||
|
||
The module's database connection is then configured by setting environment | ||
variables: | ||
`DB_HOST`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD`, `DB_DATABASE`, | ||
`DB_MAXPOOLSIZE`, `DB_SERVER_PEM`. | ||
|
||
Once configured, start the module with: | ||
|
||
``` | ||
java -Dport=8081 -jar target/mod-settings-fat.jar | ||
``` | ||
|
||
## Running with Docker | ||
|
||
If you feel adventurous and want to run mod-settings in a docker container, build the container first: | ||
|
||
``` | ||
docker build -t mod-settings:latest . | ||
``` | ||
|
||
And run with the module port exposed (`8081` by default): | ||
|
||
``` | ||
docker run -e DB_HOST=host.docker.internal \ | ||
-e DB_USERNAME=folio \ | ||
-e DB_PASSWORD=folio \ | ||
-e DB_DATABASE=folio_modules \ | ||
-p 8081:8081 --name settings mod-settings:latest | ||
``` | ||
|
||
**Note**: The magic host `host.docker.internal` is required to access | ||
the DB and may be only available in Docker Desktop. | ||
If it's not defined you can specify it by passing | ||
`--add-host=host.docker.internal:<docker bridge net IP>` to the run command. | ||
|
||
**Note**: Those docker build and run commands do work as-is with | ||
[Colima](https://github.com/abiosoft/colima). | ||
|
||
## Additional information | ||
|
||
### Issue tracker | ||
|
||
See project [MODSET](https://issues.folio.org/browse/MODSET) | ||
at the [FOLIO issue tracker](https://dev.folio.org/guidelines/issue-tracker). | ||
|
||
### Code of Conduct | ||
|
||
Refer to the Wiki | ||
[FOLIO Code of Conduct](https://wiki.folio.org/display/COMMUNITY/FOLIO+Code+of+Conduct). | ||
|
||
### ModuleDescriptor | ||
|
||
See the [ModuleDescriptor](descriptors/ModuleDescriptor-template.json) | ||
for the interfaces that this module requires and provides, the permissions, | ||
and the additional module metadata. | ||
|
||
### API documentation | ||
|
||
API descriptions: | ||
|
||
* [OpenAPI](src/main/resources/openapi/settings.yaml) | ||
* [Schemas](src/main/resources/openapi/schemas/) | ||
|
||
Generated [API documentation](https://dev.folio.org/reference/api/#mod-settings). | ||
|
||
### Code analysis | ||
|
||
[SonarQube analysis](https://sonarcloud.io/dashboard?id=org.folio%3Amod-settings). | ||
|
||
### Download and configuration | ||
|
||
The built artifacts for this module are available. | ||
See [configuration](https://dev.folio.org/download/artifacts) for repository access, | ||
and the Docker images for [released versions](https://hub.docker.com/r/folioorg/mod-settings/) | ||
and for [snapshot versions](https://hub.docker.com/r/folioci/mod-settings/). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
--- | ||
layout: null | ||
--- | ||
|
||
# How to add new settings to the mod-settings module | ||
|
||
Mike Taylor, Index Data. [email protected] | ||
|
||
|
||
<!-- md2toc -l 2 HOWTO.md --> | ||
* [Introduction](#introduction) | ||
* [What to do](#what-to-do) | ||
* [1. Define one or more scopes](#1-define-one-or-more-scopes) | ||
* [2. Define relevant permissions for the scopes](#2-define-relevant-permissions-for-the-scopes) | ||
* [3. Assign necessary permissions to users](#3-assign-necessary-permissions-to-users) | ||
* [Summary](#summary) | ||
|
||
|
||
|
||
## Introduction | ||
|
||
When FOLIO client code (whether a UI module or a back-end module) wants to store settings, the simplest and most secure way to do this is in mod-settings. To do so requires establishing one or more scopes and creating some permissions. This document explains what is required of the client module. | ||
|
||
|
||
|
||
## What to do | ||
|
||
|
||
### 1. Define one or more scopes | ||
|
||
Settings in the mod-settings module belong to a scope, which is named by a short string. Within a given scope, each setting has a unique key, which is also a short string. | ||
|
||
There is no conept of defining a machine-readable object that is a scope -- for example, in a module descriptor. Instead, a scope exists when there are settings within that scope and permissions that provide access to them. | ||
|
||
In order to avoid multiple modules defining scopes with the same names, it is conventional for scope names to begin with the name of the module that is defining them. For example, the `mod-inventory` module might define a scope called mod-inventory` which contains all the settings used by the Inventory app. | ||
|
||
When all of a module's settings are within the same scope, it follows that a user who has permissions to access to any one setting has permission to access them all. It is almost always better for a module to define several scopes, using the module name as the first of multiple `.`-separated facets. For example, the `mod-inventory` module might define scopes `mod-inventory.admin` and `mod-inventory.prefs`, For adminstrative settings and preferences respectively, and assign permissions to allow only a few inventory users access to the administrative settings, but allow a broader group access to the pereferences. | ||
|
||
In the example above, the `mod-inventory.admin` scope might contain settings with keys like `max-loans` and `max-holds` (which should only be set by administrators); and `mod-inventory.prefs` might contain settings with keys like `search-default-language` and `search-default-resource-type`. | ||
|
||
Nothing prevents one module from naming a scope that properly belongs to another -- for example, `mod-inventory` naming the scope `mod-users.prefs`. Nothing, that is, but common sense and basic decency. **Do not do this.** | ||
|
||
One a scope has been defined, it is laborious and error-prone to change its name, as it appears in permissions, in code, and in extant settings objects. So it is worth taking some time to think about a future-proof name for a scope when starting to use it. In particular, naming scopes _only_ after the modules that define them (e.g. the `mod-inventory` scope) is likely to lead to problems when it subsequently becomes necessary for the same module to define a second scope. So it is generally best to use a multi-facet scope name even when it is (initially) the only scope defined by a module. | ||
|
||
|
||
### 2. Define relevant permissions for the scopes | ||
|
||
A scope's only manifestation in a module descriptor is in the permissions that allow user to access keys in that scope. Typically two permissions are defined: | ||
|
||
`mod-settings.global.read.SCOPE` allows a user to read settings in the named scope, and `mod-settings.global.write.SCOPE` allows a user to write settings in the named scope. For example, `mod-settings.global.read.mod-inventory.prefs` allows a user to read settings from the scope `mod-inventory.prefs`. The settings module itself enforces this requirement. | ||
|
||
Note that, although these permissions are in the `mod-settings` namespace, they are defined by the client module (e.g. in the present example `mod-inventory`). This is a unique situation in FOLIO, required by the need for `mod-settings` to determine the name of the permission to check when all it knows is the scope and the operation (read or write). | ||
|
||
(There are two more pairs of permissions that can be defined for a scope: read and write for "user", meaning a user-specific value of a setting; and for "self", meaning the current user's own user-specific value. These permissions are named `mod-settings.user.read.SCOPE`/`mod-settings.user.read.SCOPE` and `mod-settings.self.read.SCOPE`/`mod-settings.self.read.SCOPE`. These have not yet been used in real code, but are available when needed.) | ||
|
||
|
||
### 3. Assign necessary permissions to users | ||
|
||
Obviously, users must be assigned whatever permissions defined by the client module are needed for the settings access they are intended to have: in the example above, including `mod-settings.global.write.mod-inventory.prefs` to set preferences, or `mod-settings.global.read.mod-inventory.admin` to read the administrative settings. | ||
|
||
In addition to these, every user must be assigned basic permissions to use the settings modules. These include: | ||
|
||
* `mod-settings.entries.item.get` to fetch a known setting | ||
* `mod-settings.entries.item.post` to create a new setting | ||
* `mod-settings.entries.item.put` to update an existing setting | ||
* `mod-settings.entries.item.delete` to delete an existing setting | ||
* `mod-settings.entries.collection.get` to search available settings | ||
|
||
(These are of course defined by `mod-settings` and do not need to be defined by the client module.) | ||
|
||
In practice, fetching a known setting is rarely used, as the opaque ID is not usually known. Instead, users must be granted the `mod-settings.entries.collection.get` permission, which governs the `/settings/entries` entry point.This is generally used with a `query` parameter such as `scope=mod-inventory.admin and key=max-loans`. | ||
|
||
|
||
## Summary | ||
|
||
If we want a module `mod-foo` to support a domain `mod-foo.bar`, then: | ||
|
||
* It will be necessary for this module to define the permissions `mod-settings.global.read` and `mod-settings.global.write`. | ||
* Users who are to read settings in this scope will need the `mod-settings.global.read.mod-foo.bar` permission as well as `mod-settings.entries.collection.get` (and they may as well have `mod-settings.entries.item.get` too). | ||
* Users who are to write settings in this scope will need the `mod-settings.global.write.mod-foo.bar` permission as well as `mod-settings.entries.item.post`, `mod-settings.entries.item.put` and `mod-settings.entries.item.delete`. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters