diff --git a/CHANGELOG.md b/CHANGELOG.md index b45de55ea..32cacec6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,20 @@ ``` LAYMAN_CLIENT_VERSION=73a6d0b5d2138e62077d305d07b4992d020168c8 ``` +- Stop using environment variable `LAYMAN_GS_ROLE_SERVICE`, it has no effect to Layman anymore. Layman now uses [role service](doc/security.md#role-service) identified by new environment variable [LAYMAN_ROLE_SERVICE_URI](doc/env-settings.md#LAYMAN_ROLE_SERVICE_URI). The service is called `layman_role_service` on GeoServer. - Set new environment variable [LAYMAN_ROLE_SERVICE_URI](doc/env-settings.md#LAYMAN_ROLE_SERVICE_URI) -- Stop using environment variable `LAYMAN_GS_ROLE_SERVICE`, it has no effect to Layman anymore. Role service called `layman_role_service` is used now. ### Migrations and checks #### Schema migrations - [#165](https://github.com/LayerManager/layman/issues/165) Add column `role_name` to table `rights` in prime DB schema. Add constraint that exactly one of columns `role_name` and `id_user` is not null. -- [#164](https://github.com/LayerManager/layman/issues/165) Create internal GeoServer [JDBC Role Service](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/roleservices.html#jdbc-role-service) DB schema `_role_service`. +- [#165](https://github.com/LayerManager/layman/issues/165) Create DB schema `_role_service` that can be used as [role service](doc/security.md#role-service). #### Data migrations ### Changes -- [#165](https://github.com/LayerManager/layman/issues/165) New REST endpoint [GET Roles](doc/rest.md#get-roles) with list of all roles registered in [JDBC Role Service](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/roleservices.html#jdbc-role-service), that can be used in access rights. This new endpoint was added to Test Client into tab "Others". -- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) and PATCH Workspace [Layer](doc/rest.md#patch-workspace-layer)/[Map](doc/rest.md#patch-workspace-map) saves [role names](doc/models.md#role) mentioned in `access_rights.read` and `access_rights.write` parameters into DB. -- [#165](https://github.com/LayerManager/layman/issues/165) Many endpoints respect role access rights: +- [#165](https://github.com/LayerManager/layman/issues/165) Prior to this version, Layman enabled to use [usernames](doc/models.md#username) and pseudo-role `EVERYONE` in access rights. From now on, Layman accepts also [role names](doc/models.md#role). +- [#165](https://github.com/LayerManager/layman/issues/165) Roles (except of `EVERYONE`) are managed by [role service](doc/security.md#role-service). +- [#165](https://github.com/LayerManager/layman/issues/165) New REST endpoint [GET Roles](doc/rest.md#get-roles) with list of all roles registered in [role service](doc/security.md#role-service), that can be used in access rights. + - This new endpoint was added to Test Client into tab "Others". +- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) and PATCH Workspace [Layer](doc/rest.md#patch-workspace-layer)/[Map](doc/rest.md#patch-workspace-map) saves [role names](doc/models.md#role) mentioned in `access_rights.read` and `access_rights.write` parameters into [prime DB schema](doc/data-storage.md#postgresql). +- [#165](https://github.com/LayerManager/layman/issues/165) Many requests respect roles in access rights: - [GET](doc/rest.md#get-workspace-layer)/[PATCH](doc/rest.md#patch-workspace-layer)/[DELETE](doc/rest.md#delete-workspace-layer) Workspace Layer - GET Workspace Layer [Thumbnail](doc/rest.md#get-workspace-layer-thumbnail)/[Style](doc/rest.md#get-workspace-layer-style)/[Metadata Comparison](doc/rest.md#get-workspace-layer-metadata-comparison)/[Chunk](doc/rest.md#get-workspace-layer-chunk) - [GET](doc/rest.md#get-workspace-map)/[PATCH](doc/rest.md#patch-workspace-map)/[DELETE](doc/rest.md#delete-workspace-map) Workspace Map diff --git a/README.md b/README.md index ffe791b47..66dbcaa91 100644 --- a/README.md +++ b/README.md @@ -175,9 +175,11 @@ When providing **external dependencies**, check their production-related documen Within PostgreSQL, you need to provide one database for Layman and one database for Micka. For Layman, you also need to provide one user [LAYMAN_PG_USER](doc/env-settings.md#LAYMAN_PG_USER) who needs enough privileges to create new schemas in [LAYMAN_PG_DBNAME](doc/env-settings.md#LAYMAN_PG_DBNAME) database. The user also needs access to `public` schema where PostGIS must be installed. +If you are using other DB schema than [internal role service schema](doc/security.md#internal-role-service-schema) as [role service](doc/security.md#role-service), you need to provide all [admin records](doc/security.md#admin-role-service-records). + Within QGIS Server, you do not need to provide anything special. -Within GeoServer, you need to provide either admin password [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD), or one Layman user [LAYMAN_GS_USER](doc/env-settings.md#LAYMAN_GS_USER) and one layman role [LAYMAN_GS_ROLE](doc/env-settings.md#LAYMAN_GS_ROLE). If admin password is provided, Layman will create the Layman user and the Layman role automatically. URL path of the GeoServer must be `/geoserver/`. +Within GeoServer, you need to provide either admin password [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD), or one Layman user [LAYMAN_GS_USER](doc/env-settings.md#LAYMAN_GS_USER). If admin password is provided, Layman will create the Layman user automatically. URL path of the GeoServer must be `/geoserver/`. Within Redis, you need to provide two databases, one for Layman, second for Layman Test Client. Connection strings are defined by [LAYMAN_REDIS_URL](doc/env-settings.md#LAYMAN_REDIS_URL) and [LTC_REDIS_URL](doc/env-settings.md#LTC_REDIS_URL). diff --git a/doc/data-storage.md b/doc/data-storage.md index 3880e74de..2e46dfdfa 100644 --- a/doc/data-storage.md +++ b/doc/data-storage.md @@ -98,10 +98,11 @@ Data is saved to LAYMAN_DATA_DIR directory, LAYMAN_QGIS_DATA_DIR directory, and Filesystem is used as persistent data store, so data survives Layman restart. ### PostgreSQL -Layman uses directly **one database** specified by [LAYMAN_PG_DBNAME](env-settings.md#LAYMAN_PG_DBNAME) to store data. There are two kinds of schemas in such database: +Layman uses directly **one database** specified by [LAYMAN_PG_DBNAME](env-settings.md#LAYMAN_PG_DBNAME) to store data. There are three kinds of schemas in such database: - [LAYMAN_PRIME_SCHEMA](env-settings.md#LAYMAN_PRIME_SCHEMA) that holds information about - users, workspaces, and publications including access rights - data version including migration ID +- [Internal Role Service Schema](security.md#internal-role-service-schema) with table and view structure that can be used as [role service](security.md#role-service) - Schemas holding vector layer data. - One **[workspace schema](https://www.postgresql.org/docs/13/ddl-schemas.html)** is created for every created [workspace](models.md#workspace). Name of workspace schema is always the same as workspace name. - One **[table](https://www.postgresql.org/docs/13/sql-createtable.html)** is created in workspace schema for each layer published with input vector files. Name of the table is in form `layer_` with `-` replaced with `_`, e.g. `layer_96b918c6_d88c_42d8_b999_f3992b826958`. The table contains data from vector data files. @@ -115,7 +116,7 @@ Data changes made directly in vector data DB tables (both internal and external) PostgreSQL is used as persistent data store, so data survives Layman restart. ### GeoServer -**[User](https://docs.geoserver.org/2.21.x/en/user/security/webadmin/ugr.html)** and **[role](https://docs.geoserver.org/2.21.x/en/user/security/webadmin/ugr.html)** are created for every [user](models.md#user) who reserved [username](models.md#username). User name on GeoServer is the same as username on Layman. Role name is composed a `USER_`. +**[User](https://docs.geoserver.org/2.21.x/en/user/security/webadmin/ugr.html)** is created for every [user](models.md#user) who reserved [username](models.md#username). Username on GeoServer is the same as username on Layman. Two **[workspaces](https://docs.geoserver.org/2.21.x/en/user/data/webadmin/workspaces.html)** are created, each with one **[PostgreSQL datastore](https://docs.geoserver.org/2.21.x/en/user/data/app-schema/data-stores.html#postgis)**, for every [workspace](models.md#workspace) (both personal and public). First workspace is meant for [WFS](endpoints.md#web-feature-service) and has the same name as the workspace on Layman. Second workspace is meant for [WMS](endpoints.md#web-map-service) and is suffixed with `_wms`. Name of the datastore is `postgresql` for both workspaces. Every workspace-related information (including PostgreSQL datastore) is saved inside workspace. @@ -127,6 +128,6 @@ For each vector layer with QML style, **[Feature Type](https://docs.geoserver.or For each raster layer, **[Coverage Store](https://docs.geoserver.org/2.21.x/en/user/rest/api/coveragestores.html)**, **[Coverage](https://docs.geoserver.org/2.21.x/en/user/rest/api/coverages.html)**, and **[Style](https://docs.geoserver.org/2.21.x/en/user/styling/webadmin/index.html)** are created in WMS workspace. If layer is [timeseries](models.md#timeseries), Coverage Store is [ImageMosaic](https://docs.geoserver.org/2.21.x/en/user/data/raster/imagemosaic/index.html), otherwise it is [GeoTIFF](https://docs.geoserver.org/2.21.x/en/user/data/raster/geotiff.html). Names of Coverage and Style are the same as layername, name of Coverage Store is prefixed with `geotiff_` or `image_mosaic_` depending on its type. Coverage Store and Coverage points to appropriate normalized raster GeoTIFF file(s). Style contains visualization file. -Two **[access rules](https://docs.geoserver.org/2.21.x/en/user/security/layer.html)** are created for each layer in each GeoServer workspace (WFS and WMS), one for [read access right](security.md#publication-access-rights), one for [write access right](security.md#publication-access-rights). Every username from Layman's access right is represented by user's role name (i.e. `USER_`). Role `EVERYONE` is represented as `ROLE_ANONYMOUS` on GeoServer. +Two **[access rules](https://docs.geoserver.org/2.21.x/en/user/security/layer.html)** are created for each layer in each GeoServer workspace (WFS and WMS), one for [read access right](security.md#publication-access-rights), one for [write access right](security.md#publication-access-rights). Every username from Layman's access right is represented by user's role name (i.e. `USER_`). Role `EVERYONE` is represented as `ROLE_ANONYMOUS` and `ROLE_AUTHENTICATED` on GeoServer. GeoServer is used as persistent data store, so data survives Layman restart. diff --git a/doc/env-settings.md b/doc/env-settings.md index 03c698bcd..f21cfba7c 100644 --- a/doc/env-settings.md +++ b/doc/env-settings.md @@ -100,7 +100,7 @@ List of [users](models.md#user) and [roles](models.md#role) giving them permissi List of [users](models.md#user) and [roles](models.md#role) giving them permission to publish new [publication](models.md#publication) in already created [public workspace](models.md#public-workspace). ### LAYMAN_ROLE_SERVICE_URI -URL of Role Service with schema in format `postgresql://:@:/?schema=`. If you want to use internal Role Service, set it to `postgresql://:@:/?schema=_role_service` (replace variable names with their values). URL scheme must be `postgresql`. URL host must be mentioned explicitly, as well as DB schema in `schema` URL query parameter. +URL of [Role Service](security.md#role-service) with DB schema in format `postgresql://:@:/?schema=`. URL scheme must be `postgresql`. URL host must be mentioned explicitly, as well as DB schema in `schema` URL query parameter. If you want to use [internal role service schema](security.md#internal-role-service-schema) provided by Layman, set value to `postgresql://:@:/?schema=_role_service` (replace variable names with their values). ## Layman Test Client Settings diff --git a/doc/models.md b/doc/models.md index 4029b8566..46d9dffec 100644 --- a/doc/models.md +++ b/doc/models.md @@ -69,6 +69,7 @@ - User is any person who communicates with Layman REST API through any client. - User can be either authenticated, or unauthenticated (i.e. anonymous). - User is sometimes identified by [username](#username) +- List of users with usernames can be obtained by [GET Users](rest.md#get-users). ## Username - Username is a string identifying one [user](#user), so it is unique among all users. @@ -76,13 +77,17 @@ - Each user is represented by max. one username. - Username is also used to identify user's [personal workspace](#personal-workspace) when communicating with [Layman REST API](rest.md). - Username can be reserved by [PATCH Current User](rest.md#patch-current-user). +- Usernames can be used for assigning access rights. - Anonymous user has no username. ## Role - Role is any group of users. One user can be assigned to multiple roles. - Each role is identified by name that is unique among all roles. - The name is upper-case (in contrast with [username](#username)), maximum length is 64 characters. -- Roles can be used for assigning access rights. +- Role names can be used for assigning access rights. +- Existing roles can be obtained by [GET Roles](rest.md#get-roles). + - There is always listed special pseudo-role `EVERYONE` that represents every user including anonymous (unauthenticated). +- Roles (except of `EVERYONE`) are managed by [role service](security.md#role-service). ## Workspace - Workspace is folder for [publications](#publication). diff --git a/doc/rest.md b/doc/rest.md index e2ae51efb..c8b9ee680 100644 --- a/doc/rest.md +++ b/doc/rest.md @@ -856,7 +856,7 @@ HTTP status code 200 if credentials were deleted. `/rest/roles` ### GET Roles -Get list of roles. +Get list of [roles](models.md#role) available in [role service](security.md#role-service) in table `roles` except of [admin records](security.md#admin-role-service-records). Pseudo-role `EVERYONE` appear in the list too. #### Request. No action parameters. diff --git a/doc/security.md b/doc/security.md index 22fe908d6..cde601b4f 100644 --- a/doc/security.md +++ b/doc/security.md @@ -43,8 +43,7 @@ Access rights enable user to control access to publications. Access to each publ - grants deleting the publication by `DELETE` HTTP request to [multi-publication REST API endpoints](#access-to-multi-publication-endpoints) - grants WFS-T requests to the layer -Both read and write access rights contain list of [user names](models.md#username) or [role names](models.md#role). Currently, Layman accepts following roles: -- `EVERYONE`: every user including anonymous (unauthenticated) +Both read and write access rights contain list of [usernames](models.md#username) or [role names](models.md#role). Users listed in access rights, either directly or indirectly through roles, are granted to perform described actions. @@ -71,3 +70,45 @@ Access is treated by following rules: It's analogical for maps. +### Role Service +Despite of [usernames](models.md#username), [role names](models.md#role) are not controlled by Layman, but by **role service**. + +Role service can be any PostgreSQL DB schema containing table (or view, or materialized view) structure described in [GeoServer documentation](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/roleservices.html#jdbc-role-service). Furthermore, Layman has special requirements to records in the tables. There are two types of records: [admin records](#admin-role-service-records) and [business records](#business-role-service-records). No other records are allowed. + +Role service is used by both Layman and GeoServer when [access rights](#publication-access-rights) are evaluated. + +Role service is identified by [LAYMAN_ROLE_SERVICE_URI](env-settings.md#LAYMAN_ROLE_SERVICE_URI). It can contain URI to any PostgreSQL schema that meets mentioned requirements, e.g. to [internal role service schema](#internal-role-service-schema). + +#### Admin role-service records + +Admin records are needed for Layman and GeoServer to handle authorization correctly. + +- Table `roles` must contain records + +| name | parent | +|---------------------------------------------------------------------|--------| +| `ADMIN` | null | +| `GROUP_ADMIN` | null | +| value of [LAYMAN_GS_ROLE](./env-settings.md#LAYMAN_GS_ROLE) | null | +| `USER_` of every user with [username](models.md#username) | null | + +These records do not appear in [GET Roles](rest.md#get-roles). They are also not accepted in access rights. + +- Table `user_roles` must contain records + +| username | rolename | +|--------------------------------------------------------------------------------------------------|----------| +| `admin` | `ADMIN` | +| value of [LAYMAN_GS_USER](./env-settings.md#LAYMAN_GS_USER) | `ADMIN` | +| value of [LAYMAN_GS_USER](./env-settings.md#LAYMAN_GS_USER) | value of [LAYMAN_GS_ROLE](./env-settings.md#LAYMAN_GS_ROLE) | +| every [username](models.md#username)| `USER_` | +| every [username](models.md#username)| value of [LAYMAN_GS_ROLE](./env-settings.md#LAYMAN_GS_ROLE) | + +#### Business role-service records + +Table `roles` may contain other records that appear in [GET Roles](rest.md#get-roles) and are accepted in access rights. Their `name` must match to regular expression `[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*` and `parent` must be null. Names `ROLE_ADMINISTRATOR`, `ROLE_GROUP_ADMIN`, `ROLE_AUTHENTICATED`, `ROLE_ANONYMOUS`, and `EVERYONE` are forbidden. + +Table `user_roles` may contain other records that connects [users](models.md#user) with [roles](#role-service). + +#### Internal Role Service Schema +Layman provides PostgreSQL DB schema `_role_service` that can be used as the role service. The schema contain all necessary [admin records](#admin-role-service-records) and (by default) no [business records](#business-role-service-records). Business records can be added manually to tables `bussiness_roles` and `bussiness_user_roles`.