diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index e4c86969e0..b8cbae2f23 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -18,7 +18,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 - name: Setup Go environment uses: actions/setup-go@v4 with: diff --git a/.github/workflows/compose.yml b/.github/workflows/compose.yml index aa01a4c38d..0197928757 100644 --- a/.github/workflows/compose.yml +++ b/.github/workflows/compose.yml @@ -21,7 +21,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: ${{ inputs.submodules }} - name: Download image diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ecd1ecfc96..bae63b1ec7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -27,7 +27,7 @@ jobs: image: ${{ steps.build.outputs.imageid }} steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 - name: Set up QEMU if: inputs.platforms != '' uses: docker/setup-qemu-action@v2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 272845a5d9..43f6b7ac8e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 - name: Setup Go environment uses: actions/setup-go@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9f447c23a..d682ab8a62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go environment id: go uses: actions/setup-go@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33abc742db..71f486522b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go environment uses: actions/setup-go@v4 with: @@ -39,7 +39,7 @@ jobs: runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go environment uses: actions/setup-go@v4 with: diff --git a/.gitignore b/.gitignore index 8202949ebe..789e187529 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,4 @@ toolchain/ logs_test/ tmp/ +public/ diff --git a/changelog/unreleased/eos-perms.md b/changelog/unreleased/eos-perms.md new file mode 100644 index 0000000000..0ac6f1a184 --- /dev/null +++ b/changelog/unreleased/eos-perms.md @@ -0,0 +1,6 @@ +Bugfix: fixed permission mapping to EOS ACLs + +This is to remove "m" and "q" flags in EOS ACLs +for regular write shares (no re-sharing). + +https://github.com/cs3org/reva/pull/4667 diff --git a/changelog/unreleased/eos-userquota.md b/changelog/unreleased/eos-userquota.md new file mode 100644 index 0000000000..b9edef6418 --- /dev/null +++ b/changelog/unreleased/eos-userquota.md @@ -0,0 +1,6 @@ +Enhancement: differentiate quota for user types in EOS + +We now assign a different initial quota to users depending +on their type, whether PRIMARY or not. + +https://github.com/cs3org/reva/pull/4720 diff --git a/changelog/unreleased/fix-auth-log.md b/changelog/unreleased/fix-auth-log.md new file mode 100644 index 0000000000..0f73303bb4 --- /dev/null +++ b/changelog/unreleased/fix-auth-log.md @@ -0,0 +1,6 @@ +Bugfix: auth: increase verbosity of oidc parsing errors + +This is to help further debugging of auth issues. +An unrelated error reporting was also fixed. + +https://github.com/cs3org/reva/pull/4599 diff --git a/changelog/unreleased/ocm-access.md b/changelog/unreleased/ocm-access.md new file mode 100644 index 0000000000..c99a7e2b68 --- /dev/null +++ b/changelog/unreleased/ocm-access.md @@ -0,0 +1,8 @@ +Enhancement: ocm: support bearer token access + +This PR adds support for accessing remote OCM 1.1 shares via bearer token, +as opposed to having the shared secret in the URL only. +In addition, the OCM client package is now part of the OCMD server package, +and the Discover methods have been all consolidated in one place. + +https://github.com/cs3org/reva/pull/4670 diff --git a/docker/Dockerfile.revad-ceph b/docker/Dockerfile.revad-ceph index a190c20199..ff895eed59 100644 --- a/docker/Dockerfile.revad-ceph +++ b/docker/Dockerfile.revad-ceph @@ -20,6 +20,10 @@ FROM quay.io/ceph/ceph:v18 RUN mkdir -p /etc/selinux/config +# this is a workaround as the Ceph docker image is still based on CentOS 8 Stream, which is EOL +RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* +RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* + RUN dnf update --exclude=ceph-iscsi,chrony -y && dnf install -y \ git \ gcc \ @@ -28,12 +32,12 @@ RUN dnf update --exclude=ceph-iscsi,chrony -y && dnf install -y \ librbd-devel \ librados-devel -ADD https://go.dev/dl/go1.21.5.linux-amd64.tar.gz \ - go1.21.5.linux-amd64.tar.gz +ADD https://go.dev/dl/go1.22.2.linux-amd64.tar.gz \ + go1.22.2.linux-amd64.tar.gz RUN rm -rf /usr/local/go && \ - tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz && \ - rm go1.21.5.linux-amd64.tar.gz + tar -C /usr/local -xzf go1.22.2.linux-amd64.tar.gz && \ + rm go1.22.2.linux-amd64.tar.gz ENV PATH /go/bin:/usr/local/go/bin:$PATH ENV GOPATH /go @@ -49,8 +53,6 @@ RUN mkdir -p /go/bin && \ make revad-ceph && \ cp /go/src/github/cs3org/reva/cmd/revad/revad /usr/bin/revad -RUN cp -r examples/ceph /etc/ - RUN mkdir -p /etc/revad/ && touch /etc/revad/revad.toml EXPOSE 9999 10000 diff --git a/docs/assets/scss/styles_project b/docs/assets/scss/styles_project new file mode 100644 index 0000000000..212cd84b73 --- /dev/null +++ b/docs/assets/scss/styles_project @@ -0,0 +1,15 @@ +/* + +Add styles or override variables from the theme here. + +*/ + +$primary: #29a7df; +$secondary: #ED6A5A; +$dark: #131516; +$enable-gradients: false; +$enable-rounded: false; +$enable-shadows: false; +$td-enable-google-fonts: true; +$google_font_name: "Roboto"; +$google_font_family: "Roboto:300,300i,400,400i,700,700i"; diff --git a/docs/content/en/docs/config/grpc/interceptors/_index.md b/docs/content/en/docs/config/grpc/interceptors/_index.md index b73299cd28..7ceb7a973b 100644 --- a/docs/content/en/docs/config/grpc/interceptors/_index.md +++ b/docs/content/en/docs/config/grpc/interceptors/_index.md @@ -11,4 +11,5 @@ To configure an GRPC interceptor you need to follow this convention in the confi {{< highlight toml >}} [grpc.interceptors.interceptor_name] ... config ... +{{}} diff --git a/docs/content/en/docs/config/grpc/services/_index.md b/docs/content/en/docs/config/grpc/services/_index.md index 6c31544a64..d67d237cf1 100644 --- a/docs/content/en/docs/config/grpc/services/_index.md +++ b/docs/content/en/docs/config/grpc/services/_index.md @@ -11,4 +11,4 @@ To configure a GRPC service you need to follow this convention in the config fil {{< highlight toml >}} [grpc.services.service_name] ... config ... - +{{}} diff --git a/docs/content/en/docs/config/http/middlewares/_index.md b/docs/content/en/docs/config/http/middlewares/_index.md index b8c73308d6..912ab36fe4 100644 --- a/docs/content/en/docs/config/http/middlewares/_index.md +++ b/docs/content/en/docs/config/http/middlewares/_index.md @@ -11,4 +11,5 @@ To configure an HTTP middleware you need to follow this convention in the config {{< highlight toml >}} [http.middlewares.middleware_name] ... config ... +{{}} diff --git a/docs/content/en/docs/config/http/services/_index.md b/docs/content/en/docs/config/http/services/_index.md index f87a641e40..840e1f6776 100644 --- a/docs/content/en/docs/config/http/services/_index.md +++ b/docs/content/en/docs/config/http/services/_index.md @@ -11,4 +11,6 @@ To configure an HTTP service you need to follow this convention in the config fi {{< highlight toml >}} [http.services.service_name] ... config ... +{{}} + diff --git a/docs/content/en/docs/config/packages/notification/notificationhelper/_index.md b/docs/content/en/docs/config/packages/notification/notificationhelper/_index.md index 45101d615d..0e1a76b716 100644 --- a/docs/content/en/docs/config/packages/notification/notificationhelper/_index.md +++ b/docs/content/en/docs/config/packages/notification/notificationhelper/_index.md @@ -32,11 +32,11 @@ nats_stream = "reva-notifications" {{< /highlight >}} {{% /dir %}} -{{% dir name="templates" type="map[string]interface{}" default= %}} +{{% dir name="templates" type="map[string]interface{}" default=nil %}} Notification templates for the service. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/notification/notificationhelper/notificationhelper.go#L50) {{< highlight toml >}} [notification.notificationhelper] -templates = +templates = nil {{< /highlight >}} {{% /dir %}} diff --git a/docs/content/en/docs/config/serverless/services/notifications/_index.md b/docs/content/en/docs/config/serverless/services/notifications/_index.md index fe98ffbd5f..9b42b2b6cb 100644 --- a/docs/content/en/docs/config/serverless/services/notifications/_index.md +++ b/docs/content/en/docs/config/serverless/services/notifications/_index.md @@ -16,11 +16,11 @@ nats_address = "" {{< /highlight >}} {{% /dir %}} -{{% dir name="nats_token" type="string" default="The token to authenticate against the NATS server" %}} - [[Ref]](https://github.com/cs3org/reva/tree/master/internal/serverless/services/notifications/notifications.go#L48) +{{% dir name="nats_token" type="string" default="" %}} +The token to authenticate against the NATS server [[Ref]](https://github.com/cs3org/reva/tree/master/internal/serverless/services/notifications/notifications.go#L48) {{< highlight toml >}} [serverless.services.notifications] -nats_token = "The token to authenticate against the NATS server" +nats_token = "" {{< /highlight >}} {{% /dir %}} @@ -32,11 +32,11 @@ nats_prefix = "reva-notifications" {{< /highlight >}} {{% /dir %}} -{{% dir name="handlers" type="map[string]map[string]interface{}" default= %}} +{{% dir name="handlers" type="map[string]map[string]interface{}" default=nil %}} Settings for the different notification handlers. [[Ref]](https://github.com/cs3org/reva/tree/master/internal/serverless/services/notifications/notifications.go#L50) {{< highlight toml >}} [serverless.services.notifications] -handlers = +handlers = nil {{< /highlight >}} {{% /dir %}} diff --git a/docs/content/en/docs/tutorials/cephfs-tutorial.md b/docs/content/en/docs/tutorials/cephfs-tutorial.md new file mode 100644 index 0000000000..4975507777 --- /dev/null +++ b/docs/content/en/docs/tutorials/cephfs-tutorial.md @@ -0,0 +1,135 @@ +--- +title: "Setting up Reva with CephFS" +linkTitle: "Setting up Reva with CephFS" +weight: 10 +description: > + Setting up Reva with a CephFS cluster +--- + +This is a guide on how to set up Reva in your local environment and connect it to an existing CephFS cluster. + +For questions on this tutorial plase refer to https://github.com/cs3org/reva/discussions/4610 + +### 1. CephFS setup +You need to have an existing CephFS installation in the machine where you will deploy Reva. +Even though is not needed for Reva to have CephFS mounted on the machine where Reva will run, we highly recommend it +as it will make grasping the concepts much easier. + +For this tutorial, we have a Ceph mount exposed under the mountpoint `/cephfs`. + +``` +$ cat /etc/fstab | grep cephfs +cernbox@.cernbox=/ /cephfs ceph rbytes +``` + +``` +$ df -h | grep ceph +10.81.22.151:6789,10.81.22.161:6789,10.81.22.171:6789:/ 1.3P 650G 1.2P 1% /cephfs +``` + +The ceph configuration lives under `/etc/ceph`. + +``` +$ tree /etc/ceph/ +/etc/ceph/ +├── ceph.client.cernbox.keyring +├── ceph.conf +└── rbdmap +``` + +Your cluster details will differ, this is just an example configuration file. +``` +$ cat /etc/ceph/ceph.conf +[global] +auth_client_required=cephx +fsid=f5195e24-158c-11ee-b338-5ced8c61b074 +mon_host=[v2:10.81.22.151:3300/0,v1:10.81.22.151:6789/0],[v2:10.81.22.161:3300/0,v1:10.81.22.161:6789/0],[v2:10.81.22.171:3300/0,v1:10.81.22.171:6789/0] +``` + +``` +cat /etc/ceph/ceph.client.cernbox.keyring +[client.cernbox] +key = mycephsecretkey== +``` + +With this information we can start setting up Reva. + + + +## Reva setup + + +Follow the steps here: +https://reva.link/docs/getting-started/build-reva/ + +We also need the libcephfs library, depending on your OS the command to install will change, here is how you install it for Fedora 39: +``` +dnf install libcephfs* -y +``` + +At this step you shoudl have a local clone of the Reva software: + +``` +git clone https://github.com/cs3org/reva +cd reva +make revad-ceph +make reva +./cmd/revad/revad -v +``` + +You can copy the binaries (`reva` is the client cli and `revad` is the daemon) to a default location so is available in your PATH: +``` +cp ./cmd/revad/revad /usr/local/bin/revad +cp ./cmd/reva/reva /usr/local/bin/reva +``` + + +### Creating test users +CephFS relies on the UNIX uid and guid attributes to perform access control. +For this example, we'll create `einstein` user with `uid=4000`: + +``` +$ sudo useradd -u 4000 einstein +$ id einstein +uid=4000(einstein) gid=4000(einstein) groups=4000(einstein) +``` +### Create configuration files + +For this tutorial, we'll use two files: +- `revad.toml` (main configuration file to run reva, preconfigured for Ceph cluster) +- `test_users.json` (configuration used to store users, only `einstein` is configured) + +These files are available at https://github.com/cs3org/reva/tree/master/examples/cephfs + +Copy the `revad.toml` to `/etc/revad/revad.toml`, the default location where the reva binary will load its configuration. +Copy the `test_users.json` file to `/etc/revad/test_users.json` to match the configuration from `/etc/revad/revad.toml`. +Create directory where reva will log its outpout: `mkdir -p /var/log/revad`. + +### Run revad +Ideally you would use an init system like systemd or docker to run it, for this tutorial we run it manually: +``` +$ nohup revad & +``` + +Let's take a look at the logs: + +``` +tail /var/log/revad/revad.log +``` + +### Connect to revad + +The Reva daemon listens on port `9143` (configured in `/etc/revad/revad.toml`) +Let's use the reva client cli to connect to it: + +``` +$ reva -host localhost:9143 -insecure login basic +username: einstein +password: +OK + +$ reva whoami +``` + + + diff --git a/docs/package-lock.json b/docs/package-lock.json index 35305c603f..1c78071386 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,671 +1,1718 @@ { "name": "tech-doc-hugo", "version": "0.0.1", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { + "packages": { + "": { + "name": "tech-doc-hugo", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "hugo-extended": "^0.124.1" + }, + "devDependencies": { + "autoprefixer": "^10.2.6", + "postcss": "^8.4.31", + "postcss-cli": "^8.3.1" + } + }, + "node_modules/@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { + "dependencies": { "@babel/highlight": "^7.12.13" } }, - "@babel/helper-validator-identifier": { + "node_modules/@babel/helper-validator-identifier": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" }, - "@babel/highlight": { + "node_modules/@babel/highlight": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "requires": { + "dependencies": { "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@nodelib/fs.scandir": { + "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "@nodelib/fs.stat": { + "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "@nodelib/fs.walk": { + "node_modules/@nodelib/fs.walk": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" } }, - "@types/parse-json": { + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" + }, + "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "array-union": { + "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "at-least-node": { + "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4.0.0" + } }, - "autoprefixer": { + "node_modules/autoprefixer": { "version": "10.2.6", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.6.tgz", "integrity": "sha512-8lChSmdU6dCNMCQopIf4Pe5kipkAGj/fvTMslCsih0uHpOrXOPUEVOmYMMqmw3cekQkSD7EhIeuYl5y0BLdKqg==", "dev": true, - "requires": { + "dependencies": { "browserslist": "^4.16.6", "caniuse-lite": "^1.0.30001230", "colorette": "^1.2.2", "fraction.js": "^4.1.1", "normalize-range": "^0.1.2", "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "binary-extensions": { + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } }, - "braces": { + "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "requires": { + "dependencies": { "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "browserslist": { + "node_modules/browserslist": { "version": "4.16.6", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, - "requires": { + "dependencies": { "caniuse-lite": "^1.0.30001219", "colorette": "^1.2.2", "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" } }, - "callsites": { + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "caniuse-lite": { + "node_modules/caniuse-lite": { "version": "1.0.30001234", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001234.tgz", "integrity": "sha512-a3gjUVKkmwLdNysa1xkUAwN2VfJUJyVW47rsi3aCbkRCtbHAfo+rOsCqVw29G6coQ8gzAPb5XBXwiGHwme3isA==", - "dev": true + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/careful-downloader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/careful-downloader/-/careful-downloader-3.0.0.tgz", + "integrity": "sha512-5KMIPa0Yoj+2tY6OK9ewdwcPebp+4XS0dMYvvF9/8fkFEfvnEpWmHWYs9JNcZ7RZUvY/v6oPzLpmmTzSIbroSA==", + "dependencies": { + "debug": "^4.3.4", + "decompress": "^4.2.1", + "fs-extra": "^11.1.1", + "got": "^12.6.0", + "is-path-inside": "^4.0.0", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/careful-downloader/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } }, - "chalk": { + "node_modules/chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, - "requires": { + "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" } }, - "cliui": { + "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colorette": { + "node_modules/colorette": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, - "cosmiconfig": { + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, - "requires": { + "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" } }, - "dependency-graph": { + "node_modules/dependency-graph": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz", "integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6.0" + } }, - "dir-glob": { + "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "requires": { + "dependencies": { "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "electron-to-chromium": { + "node_modules/electron-to-chromium": { "version": "1.3.748", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.748.tgz", "integrity": "sha512-fmIKfYALVeEybk/L2ucdgt7jN3JsbGtg3K9pmF/MRWgkeADBI1VSAa5IzdG2gZwTxsnsrFtdMpOTSM5mrBRKVQ==", "dev": true }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "error-ex": { + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "escalade": { + "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "engines": { + "node": ">=0.8.0" + } }, - "fast-glob": { + "node_modules/fast-glob": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.0", "merge2": "^1.3.0", "micromatch": "^4.0.2", "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8" } }, - "fastq": { + "node_modules/fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, - "requires": { + "dependencies": { "reusify": "^1.0.4" } }, - "fill-range": { + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" } }, - "fraction.js": { + "node_modules/fraction.js": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", - "dev": true + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "fs-extra": { + "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "requires": { + "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-stdin": { + "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "globby": { + "node_modules/globby": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", "dev": true, - "requires": { + "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, - "has-flag": { + "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/hugo-extended": { + "version": "0.124.1", + "resolved": "https://registry.npmjs.org/hugo-extended/-/hugo-extended-0.124.1.tgz", + "integrity": "sha512-GaF/wOTSrfobyVwd46/0KSwVPQ76ZEU4BnKj5rrLyYtGDwFM4UXGdtTa9z8xMI4DF9RvCV6UIvU0cvkcDEl+aA==", + "hasInstallScript": true, + "dependencies": { + "careful-downloader": "^3.0.0", + "log-symbols": "^5.1.0", + "read-pkg-up": "^9.1.0" + }, + "bin": { + "hugo": "lib/cli.js", + "hugo-extended": "lib/cli.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "ignore": { + "node_modules/ignore": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4" + } }, - "import-cwd": { + "node_modules/import-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", "dev": true, - "requires": { + "dependencies": { "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "import-fresh": { + "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "requires": { + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "import-from": { + "node_modules/import-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "is-arrayish": { + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==" + }, + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, - "js-tokens": { + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, - "jsonfile": { + "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", + "dependencies": { "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "lines-and-columns": { + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "lodash.difference": { + "node_modules/lodash.difference": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", "dev": true }, - "lodash.forown": { + "node_modules/lodash.forown": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-4.4.0.tgz", "integrity": "sha1-hRFc8E9z75ZuztUlEdOJPMRmg68=", "dev": true }, - "lodash.get": { + "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, - "lodash.groupby": { + "node_modules/lodash.groupby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=", "dev": true }, - "lodash.sortby": { + "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, - "merge2": { + "node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" } }, - "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } }, - "node-releases": { + "node_modules/node-releases": { "version": "1.1.72", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", "dev": true }, - "normalize-path": { + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "normalize-range": { + "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } }, - "parent-module": { + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pify": { + "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "postcss": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", - "integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==", + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, - "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "postcss-cli": { + "node_modules/postcss-cli": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-8.3.1.tgz", "integrity": "sha512-leHXsQRq89S3JC9zw/tKyiVV2jAhnfQe0J8VI4eQQbUjwIe0XxVqLrR+7UsahF1s9wi4GlqP6SJ8ydf44cgF2Q==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "chokidar": "^3.3.0", "dependency-graph": "^0.9.0", @@ -678,180 +1725,592 @@ "read-cache": "^1.0.0", "slash": "^3.0.0", "yargs": "^16.0.0" + }, + "bin": { + "postcss": "bin/postcss" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "postcss-load-config": { + "node_modules/postcss-load-config": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.0.1.tgz", "integrity": "sha512-/pDHe30UYZUD11IeG8GWx9lNtu1ToyTsZHnyy45B4Mrwr/Kb6NgYl7k753+05CJNKnjbwh4975amoPJ+TEjHNQ==", "dev": true, - "requires": { + "dependencies": { "cosmiconfig": "^7.0.0", "import-cwd": "^3.0.0" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "postcss-reporter": { + "node_modules/postcss-reporter": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.2.tgz", "integrity": "sha512-JyQ96NTQQsso42y6L1H1RqHfWH1C3Jr0pt91mVv5IdYddZAE9DUZxuferNgk6q0o6vBVOrfVJb10X1FgDzjmDw==", "dev": true, - "requires": { + "dependencies": { "colorette": "^1.2.1", "lodash.difference": "^4.5.0", "lodash.forown": "^4.4.0", "lodash.get": "^4.4.2", "lodash.groupby": "^4.6.0", "lodash.sortby": "^4.7.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "postcss-value-parser": { + "node_modules/postcss-value-parser": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, - "pretty-hrtime": { + "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "read-cache": { + "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", "dev": true, - "requires": { + "dependencies": { "pify": "^2.3.0" } }, - "readdirp": { + "node_modules/read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" }, - "resolve-from": { + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "run-parallel": { + "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { "queue-microtask": "^1.2.2" } }, - "slash": { + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "dev": true + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "string-width": { + "node_modules/string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dependencies": { + "is-natural-number": "^4.0.1" } }, - "supports-color": { + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "to-regex-range": { + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "universalify": { + "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true + "engines": { + "node": ">= 10.0.0" + } }, - "wrap-ansi": { + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yaml": { + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -859,13 +2318,39 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "20.2.7", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/docs/package.json b/docs/package.json index a29f9d640d..b31cc2babf 100644 --- a/docs/package.json +++ b/docs/package.json @@ -16,10 +16,12 @@ "url": "https://github.com/bep/tech-doc-hugo/issues" }, "homepage": "https://github.com/bep/tech-doc-hugo#readme", - "dependencies": {}, "devDependencies": { "autoprefixer": "^10.2.6", - "postcss-cli": "^8.3.1", - "postcss": "^8.4.31" + "postcss": "^8.4.31", + "postcss-cli": "^8.3.1" + }, + "dependencies": { + "hugo-extended": "^0.124.1" } } diff --git a/docs/themes/docsy b/docs/themes/docsy index 1a09abe6d7..b95a4948ee 160000 --- a/docs/themes/docsy +++ b/docs/themes/docsy @@ -1 +1 @@ -Subproject commit 1a09abe6d7eaf02e44f79de3eadf8178a3792e81 +Subproject commit b95a4948ee42e601d2685a334e0b2362e31820ef diff --git a/examples/ceph/ceph.conf b/examples/ceph/ceph.conf deleted file mode 100644 index f2daaa8de5..0000000000 --- a/examples/ceph/ceph.conf +++ /dev/null @@ -1,3 +0,0 @@ -[global] - fsid = '8aaa35c4-75dc-42e5-a812-cbc1cdfd3323' - mon_host = '[v2:188.184.96.178:3300/0,v1:188.184.96.178:6789/0] [v2:188.185.88.76:3300/0,v1:188.185.88.76:6789/0] [v2:188.185.126.6:3300/0,v1:188.185.126.6:6789/0]' diff --git a/examples/ceph/keyring b/examples/ceph/keyring deleted file mode 100644 index 9e555cc1b2..0000000000 --- a/examples/ceph/keyring +++ /dev/null @@ -1,2 +0,0 @@ -[client.admin] - key = 'AQAu88Fg5iekGhAAeVP0Td05PuybytuRJgBRqA==' diff --git a/examples/cephfs/revad.toml b/examples/cephfs/revad.toml new file mode 100644 index 0000000000..1e8a01ba04 --- /dev/null +++ b/examples/cephfs/revad.toml @@ -0,0 +1,143 @@ +[log] +output = "/var/log/revad/revad.log" +mode = "json" +level = "debug" + +[shared] +gatewaysvc = "localhost:9142" +jwt_secret = "$JWTSECRET" +skip_user_groups_in_token = true + +[grpc] +address = "localhost:9142" + +[grpc.services.gateway] +commit_share_to_storage_grant = true +disable_home_creation_on_login = false +transfer_expires = 86400 # seconds +etag_cache_ttl = 0 +#create_home_cache_ttl = 86400 # seconds +transfer_shared_secret = "$TRANSFERSECRET" +authregistrysvc = "localhost:9142" +storageregistrysvc = "localhost:9142" +userprovidersvc = "localhost:9142" +datagateway = "https://$HOSTNAME/datagateway" + +[grpc.services.storageprovider] +driver = "cephfs" +mount_path = "/" +mount_id = "cephfs" +data_server_url = "http://localhost:9143/data" + +[grpc.services.storageprovider.drivers.cephfs] +root = "/" +config = "/etc/ceph/ceph.conf" +keyring = "/etc/ceph/ceph.client.cernbox.keyring" +client_id = "cernbox" +disable_home = false +enable_recycle = false +dir_perms = 0o777 +file_perms = 0o666 +user_layout = "/users/{{.Username}}" + +[grpc.services.gateway.token_managers.jwt] +expires = 1500 + +[grpc.services.authregistry] +driver = "static" + +[grpc.services.authregistry.drivers.static.rules] +basic = "localhost:9142" +bearer = "localhost:9158" + +[grpc.services.storageregistry] +driver = "static" + +[grpc.services.storageregistry.drivers.static] +#home_provider = "/" + +[grpc.services.storageregistry.drivers.static.rules] +"/" = {"address" = "localhost:9142"} + +[grpc.services.userprovider] +driver = "json" + +[grpc.services.userprovider.drivers.json] +users = "/etc/revad/test_users.json" + +[grpc.services.authprovider] +auth_manager = "json" + +[grpc.services.authprovider.auth_managers.json] +users = "/etc/revad/test_users.json" + +[http] +address = "0.0.0.0:9143" + +[http.middlewares.auth] +credential_chain = ["basic", "bearer"] +token_strategy_chain = ["bearer", "header"] + +[http.middlewares.auth.credentials_by_user_agent] +"mirall" = "basic" + +[http.middlewares.cors] +allowed_origins = ["*"] +allowed_methods = ["OPTIONS", "LOCK", "GET", "HEAD", "POST", "DELETE", "PROPPATCH", "COPY", "MOVE", "UNLOCK", "PROPFIND", "MKCOL", "REPORT", "SEARCH", "PUT"] +allowed_headers = ["Accept", "Accept-Language", "Authorization", "Content-Language", "Content-Type", "Depth", "OCS-APIREQUEST", "Referer", "sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "User-Agent", "X-Requested-With"] +debug = true +exposed_headers = [] + +[http.services.datagateway] +transfer_shared_secret = "$TRANSFERSECRET" +timeout = 86400 +insecure = true +prefix = "datagateway" + + +[http.services.ocdav] +files_namespace = "/" +webdav_namespace = "/" +timeout = 86400 +insecure = true +enable_http_tpc = false + +[http.services.ocs.config] +version = "1.8" +website = "reva" +host = "https://$HOSTNAME:443" +ssl = "false" + +[http.services.ocs.capabilities.capabilities.core] +poll_interval = 60 +webdav_root = "remote.php/webdav/" +status = { installed = true, maintenance = false, nwwsaDbUpgrade = false, version = "10.0.11.5", versionstring = "10.0.11", edition = "community", productname = "reva", hostname = "" } +support_url_signing = false + +[http.services.ocs.capabilities.capabilities.checksums] +supported_types = ["sha1", "md5", "adler32"] + +[http.services.ocs.capabilities.capabilities.dav] +chunkingParallelUploadDisabled = false + +[http.services.ocs.capabilities.capabilities.files] +private_links = false +bigfilechunking = true +blacklisted_files = [ ] +undelete = true +versioning = true + +[http.services.dataprovider] +driver = "cephfs" + +[http.services.dataprovider.drivers.cephfs] +root = "/" +config = "/etc/ceph/ceph.conf" +keyring = "/etc/ceph/ceph.client.cernbox.keyring" +client_id = "cernbox" +disable_home = false +enable_recycle = false +dir_perms = 0o777 +file_perms = 0o666 +user_layout = "/users/{{.Username}}" + diff --git a/examples/cephfs/test_users.json b/examples/cephfs/test_users.json new file mode 100644 index 0000000000..c213e9e2ec --- /dev/null +++ b/examples/cephfs/test_users.json @@ -0,0 +1,18 @@ +[ + { + "id": { + "opaque_id": "einstein", + "type": 1 + }, + "username": "einstein", + "secret": "relativity", + "mail": "example@org", + "display_name": "Einstein", + "groups": [ + "sailing-lovers", + "violin-haters", + "physics-lovers" + ], + "uid_number": 4000 + } +] diff --git a/examples/cernbox/cernbox.toml b/examples/cernbox/cernbox.toml new file mode 100644 index 0000000000..3f7b47b30d --- /dev/null +++ b/examples/cernbox/cernbox.toml @@ -0,0 +1,406 @@ +## Example configuration to run a dockerized CERNBox for ScienceMesh tests +# +# The following variables are actualized by the scripts in tests/sciencemesh/scripts + +[vars] +internal_gateway = "your.revad.org" +provider_domain = "your.revad.org" +external_reva_endpoint = "https://your.nginx.org" # append here any route if applicable +wopi_endpoint = "http://your.wopi.org:8880" +ocmshares_json_file = "/var/tmp/reva/shares_cernbox.json" +machine_api_key = "machine-api-key" +wopi_shared_secret = "shared-secret-2" + +[http] +certfile = "/etc/tls/your.revad.ssl.crt" +keyfile = "/etc/tls/your.revad.ssl.key" + +[log] +level = "debug" + +[shared] +gatewaysvc = "{{ vars.internal_gateway }}:19000" +jwt_secret = "reva-secret" + +[grpc.services.gateway] +address = ":19000" +authregistrysvc = "{{ grpc.services.authregistry.address }}" +appregistrysvc = "{{ grpc.services.appregistry.address }}" +storageregistrysvc = "{{ grpc.services.storageregistry.address }}" +preferencessvc = "{{ grpc.services.userprovider.address }}" +userprovidersvc = "{{ grpc.services.userprovider.address }}" +usershareprovidersvc = "{{ grpc.services.usershareprovider.address }}" +publicshareprovidersvc = "{{ grpc.services.publicshareprovider.address }}" +ocmcoresvc = "{{ grpc.services.ocmcore.address }}" +ocmshareprovidersvc = "{{ grpc.services.ocmshareprovider.address }}" +ocminvitemanagersvc = "{{ grpc.services.ocminvitemanager.address }}" +ocmproviderauthorizersvc = "{{ grpc.services.ocmproviderauthorizer.address }}" +datagateway = "https://{{ http.services.datagateway.address }}/data" + +transfer_expires = 6 # give it a moment +commit_share_to_storage_grant = true +commit_share_to_storage_ref = true + + +### APPS ### + +[grpc.services.appregistry] +driver = "static" + +[grpc.services.appregistry.drivers.static] +mime_types = [ + {"mime_type" = "text/plain", "extension" = "txt", "name" = "Text file", "description" = "Text file", "allow_creation" = true}, + {"mime_type" = "text/markdown", "extension" = "md", "name" = "Markdown file", "description" = "Markdown file", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.text", "extension" = "odt", "name" = "OpenDocument", "description" = "OpenDocument text document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.spreadsheet", "extension" = "ods", "name" = "OpenSpreadsheet", "description" = "OpenDocument spreadsheet document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.presentation", "extension" = "odp", "name" = "OpenPresentation", "description" = "OpenDocument presentation document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.jupyter", "extension" = "ipynb", "name" = "Jupyter Notebook", "description" = "Jupyter Notebook"} +] + +[[grpc.services.appprovider]] +driver = "wopi" +custom_mime_types_json = "/etc/revad/custom-mime-types-demo.json" +mime_types = ["application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.presentation", "text/rtf"] +app_provider_url = "{{ grpc.services.appprovider[0].address }}" +language = "en-GB" + +[grpc.services.appprovider.drivers.wopi] +iop_secret = "{{ vars.wopi_shared_secret }}" +wopi_url = "{{ vars.wopi_endpoint }}" +app_name = "Collabora" +app_url = "http://collabora.docker:9980" +app_int_url = "http://collabora.docker:9980" + + +# [[grpc.services.appprovider]] +# driver = "wopi" +# custom_mime_types_json = "/etc/revad/custom-mime-types-demo.json" +# mime_types = ["text/markdown", "application/compressed-markdown", "text/plain"] +# app_provider_url = "{{ grpc.services.appprovider[1].address }}" +# +# [grpc.services.appprovider.drivers.wopi] +# iop_secret = "{{ vars.wopi_shared_secret }}" +# wopi_url = "{{ vars.wopi_endpoint }}" +# app_name = "CodiMD" +# app_url = "https://codimd.docker" +# app_int_url = "https://codimd.docker" + + +### AUTH ### + +[grpc.services.authregistry] +driver = "static" + +[grpc.services.authregistry.drivers.static.rules] +basic = "{{ grpc.services.authprovider[0].address }}" +bearer = "{{ grpc.services.authprovider[0].address }}" +machine = "{{ grpc.services.authprovider[1].address }}" +ocmshares = "{{ grpc.services.authprovider[2].address }}" + +[[grpc.services.authprovider]] +auth_manager = "oidc" + +[grpc.services.authprovider.auth_managers.json] +users = "/etc/revad/users.demo.json" + +[grpc.services.authprovider.auth_managers.oidc] +issuer = "https://idp.docker:8443/realms/cernbox" +uid_claim = "cern_uid" +gid_claim = "cern_gid" +gatewaysvc = "{{ vars.internal_gateway }}:19000" + +[[grpc.services.authprovider]] +auth_manager = "machine" + +[grpc.services.authprovider.auth_managers.machine] +api_key = "{{ vars.machine_api_key }}" +gateway_addr = "{{ vars.internal_gateway }}:19000" + +[[grpc.services.authprovider]] +auth_manager = "ocmshares" + + +### STORAGE ### + +[grpc.services.storageregistry] +driver = "static" + +[grpc.services.storageregistry.drivers.static] +home_provider = "/" + +[grpc.services.storageregistry.drivers.static.rules] +"/" = {"address" = "{{ grpc.services.storageprovider[0].address }}"} +"localhome" = {"address" = "{{ grpc.services.storageprovider[0].address }}"} +"/ocm" = {"address" = "{{ grpc.services.storageprovider[1].address }}"} +"ocm" = {"address" = "{{ grpc.services.storageprovider[1].address }}"} +"/sciencemesh" = { address = "{{ grpc.services.storageprovider[2].address }}" } +"sciencemesh" = { address = "{{ grpc.services.storageprovider[2].address }}" } + +[[grpc.services.storageprovider]] +driver = "localhome" +mount_path = "/" +mount_id = "localhome" +expose_data_server = true +data_server_url = "https://localhost:{{ http.services.dataprovider[0].address.port }}/data" +enable_home_creation = true + +[grpc.services.storageprovider.drivers.localhome] +user_layout = "{{.Username}}" + +[[grpc.services.storageprovider]] +driver = "ocmoutcoming" +mount_id = "ocm" +mount_path = "/ocm" +expose_data_server = true +enable_home_creation = false +data_server_url = "{{ vars.external_reva_endpoint }}/data" + +[grpc.services.storageprovider.drivers.ocmoutcoming] +machine_secret = "{{ vars.machine_api_key }}" + +[[grpc.services.storageprovider]] +driver = "ocmreceived" +mount_path = "/sciencemesh" +mount_id = "sciencemesh" +expose_data_server = true +# this must be accessible from the GUI, therefore the hostname will be replaced +data_server_url = "https://your.revad.org:{{ http.services.dataprovider[2].address.port }}/data" + + +### OTHER PROVIDERS ### + +[grpc.services.usershareprovider] +driver = "memory" + +[grpc.services.publicshareprovider] +driver = "memory" + +[grpc.services.preferences] + +[grpc.services.ocmcore] +driver = "json" + +[grpc.services.ocmcore.drivers.json] +# Note that ocmcore and ocmshareprovider should use the same file for storing the shares. +file = "{{ vars.ocmshares_json_file }}" + +[grpc.services.ocminvitemanager] +driver = "json" +provider_domain = "{{ vars.provider_domain }}" + +[grpc.services.ocmshareprovider] +driver = "json" +provider_domain = "{{ vars.provider_domain }}" +webdav_endpoint = "{{ vars.external_reva_endpoint }}" +webdav_prefix = "{{ vars.external_reva_endpoint }}/remote.php/dav/files" +webapp_template = "{{ vars.external_reva_endpoint }}/external/sciencemesh/{{.Token}}/{relative-path-to-shared-resource}" + +[grpc.services.ocmshareprovider.drivers.json] +file = "{{ vars.ocmshares_json_file }}" + +[grpc.services.ocmproviderauthorizer] +driver = "open" # pure OCM, all remote shares are accepted +#driver = "json" # to enable sciencemesh + +[grpc.services.ocmproviderauthorizer.drivers.json] +# this is used by the docker-based test deployment, not in production +providers = "/etc/revad/providers.testnet.json" +verify_request_hostname = true + +[grpc.services.userprovider] +driver = "json" + +[grpc.services.userprovider.drivers.json] +users = "/etc/revad/users.demo.json" + +[grpc.services.datatx] +txdriver = "rclone" +storagedriver = "json" +remove_transfer_on_cancel = true + +[grpc.services.datatx.txdrivers.rclone] +# rclone endpoint +endpoint = "http://rclone.docker" +# basic auth is used +auth_user = "rcloneuser" +auth_pass = "eilohtho9oTahsuongeeTh7reedahPo1Ohwi3aek" +auth_header = "x-access-token" +job_status_check_interval = 2000 +job_timeout = 120000 +storagedriver = "json" +remove_transfer_job_on_cancel = true + +[grpc.services.datatx.storagedrivers.json] +file = "" + +[grpc.services.datatx.txdrivers.rclone.storagedrivers.json] +file = "" + + +### HTTP ENDPOINTS ### + +[http.middlewares.auth] +credential_chain = ["publicshares", "basic", "bearer"] +token_strategy_chain = ["bearer", "header"] + +[http.middlewares.auth.credentials_by_user_agent] +"mirall" = "basic" + +[http.services.appprovider] +address = ":443" + +[http.services.datagateway] +address = ":443" + +[[http.services.dataprovider]] +driver = "localhome" + +[http.services.dataprovider.drivers.localhome] +user_layout = "{{.Username}}" + +[[http.services.dataprovider]] +address = ":443" +driver = "ocmoutcoming" + +[http.services.dataprovider.drivers.ocmoutcoming] +machine_secret = "{{ vars.machine_api_key }}" + +[[http.services.dataprovider]] +driver = "ocmreceived" + +[http.services.sciencemesh] +address = ":443" +provider_domain = "{{ vars.provider_domain }}" +mesh_directory_url = "https:/meshdir.docker/meshdir" +ocm_mount_point = "/sciencemesh" + +[http.services.sciencemesh.smtp_credentials] +disable_auth = true +sender_mail = "sciencemesh@{{ vars.provider_domain }}" +smtp_server = "smtp.{{ vars.provider_domain }}" +smtp_port = 25 + +[http.services.wellknown.ocmprovider] +address = ":443" +ocm_prefix = "ocm" +provider = "Reva for CERNBox" +endpoint = "{{ vars.external_reva_endpoint }}" +enable_webapp = true +enable_datatx = true + +[http.services.ocm] +address = ":443" +prefix = "ocm" + +[http.services.ocm.config] +host = "{{ vars.provider_domain }}" + +# OCS +[http.services.ocs] +address = ":443" +prefix = "ocs" +share_prefix = "/" +home_namespace = "/" +additional_info_attribute = "{{.Username}} ({{.Mail}})" +cache_warmup_driver = "first-request" +resource_info_cache_ttl = 60 +resource_info_cache_type = "memory" + +[http.services.ocs.config] +version = "1.8" +website = "reva" +host = "https://localhost" +contact = "cernbox@cernbox.org" +ssl = "false" + +[http.services.ocs.capabilities.capabilities.core] +poll_interval = 60 +webdav_root = "remote.php/webdav" +status = { installed = true, maintenance = false, nwwsaDbUpgrade = false, version = "10.0.11.5", versionstring = "10.0.11", edition = "community", productname = "reva", hostname = "" } +support_url_signing = false + +[http.services.ocs.capabilities.capabilities.checksums] +supported_types = ["sha1", "md5", "adler32"] + +[http.services.ocs.capabilities.capabilities.files] +private_links = false +bigfilechunking = false +blacklisted_files = [] +undelete = true +versioning = true +archivers = [ + { enabled = true, version = "2.0.0", formats = [ + "tar", + "zip", + ], archiver_url = "/archiver", max_num_files = "10000", max_size = "1073741824" }, +] +favorites = true + +[http.services.ocs.capabilities.capabilities.dav] + +[http.services.ocs.capabilities.capabilities.files_sharing] +api_enabled = true +resharing = true +deny_access = true +resharing_default = false +group_sharing = true +auto_accept_share = true +share_with_group_members_only = true +share_with_membership_groups_only = true +default_permissions = 22 +search_min_length = 3 + +[http.services.ocs.capabilities.capabilities.files_sharing.public] +enabled = true +send_mail = true +social_share = true +upload = true +multiple = true +supports_upload_only = true +password = { enforced = false, enforced_for = { read_only = false, read_write = false, upload_only = false } } +expire_date = { enabled = true } +can_edit = true +can_contribute = false + +[http.services.ocs.capabilities.capabilities.files_sharing.user] +send_mail = true +profile_picture = false +settings = [{ enabled = true, version = "1.0.0" }] + +[http.services.ocs.capabilities.capabilities.files_sharing.user_enumeration] +enabled = true +group_members_only = true + +[http.services.ocs.capabilities.capabilities.files_sharing.federation] +outgoing = true +incoming = true + +[http.services.ocs.capabilities.capabilities.spaces] +enabled = false +projects = true + +[http.services.ocs.capabilities.version] +edition = "reva" +major = 10 +minor = 0 +micro = 11 +string = "10.0.11" + +[http.services.ocdav] +address = ":443" +insecure = true + +[http.services.prometheus] +address = ":443" + +#[http.services.ui] +#address = ":443" + +[http.middlewares.cors] +allowed_origins = ["*"] +allowed_methods = ["OPTIONS", "LOCK", "GET", "HEAD", "POST", "DELETE", "PROPPATCH", "COPY", "MOVE", "UNLOCK", "PROPFIND", "MKCOL", "REPORT", "SEARCH", "PUT"] +allowed_headers = ["Accept", "Accept-Language", "Authorization", "Content-Language", "Content-Type", "Depth", "OCS-APIREQUEST", "Referer", "sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "User-Agent", "X-Requested-With"] +debug = true +exposed_headers = [] + +[http.middlewares.log] diff --git a/examples/cernbox/keycloak/cernbox.json b/examples/cernbox/keycloak/cernbox.json new file mode 100644 index 0000000000..c7fd64f6a8 --- /dev/null +++ b/examples/cernbox/keycloak/cernbox.json @@ -0,0 +1,2366 @@ +{ + "users": [ + { + "id": "einstein", + "username": "einstein", + "enabled": true, + "email": "einstein@cern.ch", + "emailVerified": true, + "firstName": "Albert", + "lastName": "Einstein", + "credentials": [ + { + "type": "password", + "value": "relativity" + } + ], + "realmRoles": [ + "user" + ], + "clientRoles": { + "account": [ + "view-profile", + "manage-account" + ] + } + }, + { + "id": "marie", + "username": "marie", + "enabled": true, + "email": "marie@cern.ch", + "emailVerified": true, + "firstName": "Marie", + "lastName": "Curie", + "credentials": [ + { + "type": "password", + "value": "radioactivity" + } + ], + "realmRoles": [ + "user" + ], + "clientRoles": { + "account": [ + "view-profile", + "manage-account" + ] + } + }, + { + "id": "richard", + "username": "richard", + "enabled": true, + "email": "richard@cern.ch", + "emailVerified": true, + "firstName": "Richard", + "lastName": "Feynman", + "credentials": [ + { + "type": "password", + "value": "superfluidity" + } + ], + "realmRoles": [ + "user" + ], + "clientRoles": { + "account": [ + "view-profile", + "manage-account" + ] + } + } + ], + "id": "d5f44848-3fec-4333-bcc5-2e69b96a7652", + "realm": "cernbox", + "displayName": "Keycloak", + "displayNameHtml": "
Keycloak
", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 60, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "1dd251c9-5c68-413f-8035-34e0336922c6", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651", + "attributes": {} + }, + { + "id": "e7ace27c-cfc7-4f34-8df8-bd32e0da15ce", + "name": "create-realm", + "description": "${role_create-realm}", + "composite": false, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651", + "attributes": {} + }, + { + "id": "4985b709-d1cd-4583-b733-71f51c9566d2", + "name": "default-roles-cernbox", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651", + "attributes": {} + }, + { + "id": "abf70cc8-2287-44d5-be0e-0307ff3c0b40", + "name": "admin", + "description": "${role_admin}", + "composite": true, + "composites": { + "realm": [ + "create-realm" + ], + "client": { + "cernbox-realm": [ + "view-authorization", + "query-groups", + "view-clients", + "view-identity-providers", + "view-realm", + "manage-authorization", + "manage-identity-providers", + "impersonation", + "query-users", + "query-realms", + "view-users", + "view-events", + "manage-users", + "manage-clients", + "manage-realm", + "create-client", + "query-clients", + "manage-events" + ] + } + }, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651", + "attributes": {} + }, + { + "id": "b2efa3fd-321d-47ae-a1c6-5efb5f0973f7", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651", + "attributes": {} + } + ], + "client": { + "cernbox-oidc": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "cac7cb03-5144-41f1-bba8-70b51b34905c", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "e1bb70a9-5439-48ca-b8d6-ef27a3b6f624", + "attributes": {} + } + ], + "cernbox-realm": [ + { + "id": "efaa23b3-3208-4346-92f9-49d36b714be0", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "19fa9af2-cd74-4889-9241-ccad973264de", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "b7514426-db73-47a8-9a99-9a93821cca24", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "cernbox-realm": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "1d65b3fc-4845-48a8-a00b-a11b5665e87c", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "2427f9b7-2d1e-4ca4-b1d8-bcd878346016", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "0b2f3aff-9562-4dc7-8b34-d0ad592307c3", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "31d61639-2493-4fa5-a8e5-6222a0204cb0", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "7a3514c1-1fdf-40a7-8b26-3bfd78d13d8f", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "fd468050-a5f7-4da9-b476-d701e228c1ba", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "9776694a-3e2d-481b-83f9-8b8468ef5cba", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "8c752cb9-e159-473d-8bd9-3db4b578204c", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "cernbox-realm": [ + "query-groups", + "query-users" + ] + } + }, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "0226e0da-1a5f-4671-aa3f-ea628ac6840b", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "720bdf01-f1eb-4f7d-a2f1-674b8fba8ae5", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "9bc4aaf1-1db4-4125-be4d-3ea9fdfdcddc", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "75e4d613-14b5-413b-b5ed-7323658d7237", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "2a3cc632-ed73-42de-a876-ed8a9d67927e", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "afc7679e-a9d4-4deb-9a32-8b19060385c0", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + }, + { + "id": "90b2fe49-3132-42b3-99a4-be0350d68f12", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "attributes": {} + } + ], + "account": [ + { + "id": "381fbd9e-352d-472a-88b3-b8dc1e593ed7", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "4fe6afe5-239c-4749-ad96-d607126aa370", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "0e82842e-091b-485a-b0b8-2f6679c29bda", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "467762f3-ef88-483d-8c03-3f7880b22400", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "9fea9466-3e06-4970-80e5-9f7afbd1eb80", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "873fd72b-e3f0-4825-a2d2-c88c362bb102", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "2f726de0-782b-4678-ab2e-c40d27a74968", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + }, + { + "id": "99141aca-0e6e-4e5a-97c1-5d255bece148", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "attributes": {} + } + ] + } + }, + "groups": [ + { + "id": "21b3d522-c35f-42da-9b35-8396c081fe13", + "name": "philosophy-haters", + "path": "/philosophy-haters", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "a3ee97a0-52ee-4dbc-9f9b-7c0f4851ad9e", + "name": "physics-lovers", + "path": "/physics-lovers", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "9d992aa0-be4c-4abc-9076-7493eec5cd5d", + "name": "polonium-lovers", + "path": "/polonium-lovers", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "3807793f-1d9b-4e79-ac30-0787127529e3", + "name": "quantum-lovers", + "path": "/quantum-lovers", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "9753c519-3a2e-4ae2-9114-eb00b3609709", + "name": "radium-lovers", + "path": "/radium-lovers", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "dce7f132-2fce-4958-9b51-c7b493133bec", + "name": "sailing-lovers", + "path": "/sailing-lovers", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + }, + { + "id": "9f434513-d831-481f-9870-5d5504a795be", + "name": "violin-haters", + "path": "/violin-haters", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + } + ], + "defaultRole": { + "id": "4985b709-d1cd-4583-b733-71f51c9566d2", + "name": "default-roles-cernbox", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "d5f44848-3fec-4333-bcc5-2e69b96a7651" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppMicrosoftAuthenticatorName", + "totpAppFreeOTPName", + "totpAppGoogleName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account", + "view-groups" + ] + } + ] + }, + "clients": [ + { + "id": "2c60f6c5-2ffb-4d0b-be37-997d79f2eac5", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/cernbox/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/cernbox/account/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "b2a1c6b7-014c-4c58-be28-4cd1575fd919", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/cernbox/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/cernbox/account/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "d71aeba9-ff27-44de-97d8-47cdded9bf61", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "026086ee-7521-4554-ab3e-c00d6a280373", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e1bb70a9-5439-48ca-b8d6-ef27a3b6f624", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "acd215fa-56d6-4617-a55a-8eb8b98af4cb", + "clientId": "cernbox-oidc", + "name": "cernbox-oidc", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "https://cernbox1.docker/*", + "https://cernbox2.docker/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "5a911287-7ef9-43c3-9eea-401a137c83b8", + "clientId": "cernbox-realm", + "name": "cernbox Realm", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "2299ba69-8859-4d59-b8ba-60d384b614d8", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/cernbox/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/cernbox/console/*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "64cf2e6f-7ae8-415e-bc2a-22e15b3b8845", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "f5343f9d-c533-4a6b-b7cf-b243f3387212", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "881c1d36-008a-4961-9986-61f99e49e4e7", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "ffe50a2d-8a86-4b7b-94b2-a1482894f6f1", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "0ac8f26d-382c-4d35-b63a-a06b90642a6c", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d61366bf-6d3c-440b-9d15-d146ee97d278", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "953283ab-f632-4cca-b71e-c68456e37e69", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "8ffcb8e2-d342-4434-8cfa-066629984272", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "49e5f7e1-614a-4ac8-b59c-7e7ca4be8c75", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "3377051b-b951-4120-a577-e9c8371dfe53", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "d89e8483-bdd2-4074-8f09-a30ae5297492", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "526a9d58-b944-4607-883c-cb853e7bf0d4", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "4db59ddf-4955-4aca-854a-83fea782de39", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "2d4f9809-38ad-4e88-b6d5-6990417b17e3", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "869c9612-3fba-4618-80ba-b166bfa2df31", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "3c9425f1-edf1-4daa-b78f-e925aa4a6e5f", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "d97c24af-3dbd-453f-aa41-d0ee4ffced63", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "284f7309-06e7-4a9f-8974-a0695418e3bc", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "053b3b02-3c69-490d-9382-be565e3cc6a0", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "02b6c73f-cc0b-416f-9c7f-c99fd68f7ce4", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "57dfff04-f55d-4e47-96d1-482f61f7d439", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "51c7f33d-1b01-4be6-96a5-7544e55a5cd1", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "f7fed9dc-6317-44e6-9b5d-56c0fa13a287", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "b7eb5197-7719-4b8e-a5ab-6a362b5a2d24", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "d498fcee-d13b-4bba-85c3-c95f2acddf02", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "1fbd2ac9-3d32-4095-b366-e75cd7aaaf76", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "ff61a997-a211-4978-9212-5c27b886582e", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "c79cedd3-a3ee-4f8a-96d7-81e514e8cbe9", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "6a2e64d1-d27e-4663-9d5e-ac0d78a3c5ea", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "71c4630e-b700-4874-a6d5-16c58a59853a", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "3e22efbe-796f-4956-87a8-7472fd49bfa3", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "a1884484-24d0-46b6-9040-ec38c2ec5571", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "0d05297b-71e5-4185-b81a-7c95f2c0eb3b", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "d3f84c0a-6962-4d49-ae88-a7f2a41aaed4", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "679fda99-b976-4c81-adc3-2cd1c687c59e", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "603e8df0-3016-410b-9816-abb32d0e3071", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "4e34e7a6-ae0e-47b0-b3c5-02a608306dd2", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "53e7d18c-7aae-4516-b1c8-89f91b7b86a7", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "47d66138-4145-480c-875d-363b5bc31838", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper" + ] + } + }, + { + "id": "96d7ddd5-fe73-4217-a753-a4e202a8adb7", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "a74b31e3-b126-4fc5-8825-96debd1f2471", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "82f1d154-61a3-4b6a-b1f3-811a2e86aa85", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "b262c386-dc0f-42cd-aefb-72d0c34a24dc", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "da1c03ae-7796-476b-b80b-76be91c5cd77", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "26eec102-eae6-463c-91e3-7333c627394c", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "648e6fc9-8363-4a1d-9f0a-d67a13c9edc9", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "cc8a50f7-176a-4776-9201-5050dc802f0e", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "ce297a71-1c56-4b0f-87c0-8818c2977e2a", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "85ce5401-1789-4e98-a14b-06ba935be78b", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "c99a1812-0f86-4120-8538-4c53f7be670f", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "4d4d9b4d-a081-4008-ad19-b81e3a8a9ab6", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "03928ec0-305d-4bba-a832-156a37250460", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "a94107c0-ae9e-4459-9b54-bdee3e5a2b2b", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "356c5010-27d7-4c7a-a055-3880d3dc41fd", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b6d4fff4-9930-4030-95ce-09f5f18f6846", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2c021c56-981a-4b35-945f-20e15305ef0a", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "28d90136-9d52-41d4-b6a5-c410879516d7", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "ed8520ee-e733-40ad-8ad0-f66b803a85a6", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "beb2fdfe-e8ad-4fe7-b0a3-1cbc7cd232e5", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "3583e59f-8e55-41fa-8eb2-61dffa918f69", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "da4eaa2e-2f57-4f3b-bf87-8bdeb25e6cc8", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "61da9559-fcc0-411b-862c-1ecff20403a0", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "75f9bd37-f7fb-44d2-a3c7-ee022e31080d", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "0daeca0e-d7dc-40b9-8116-83307bf06c06", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "fad12cb4-3a42-4cad-b589-3ff705ae41c0", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "6674479a-9f99-430c-a774-6b44e36f45b1", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "fa0fc72c-92eb-42f2-942a-0e687b9ad184", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "b07a2cd8-d159-449c-b239-5b077e075d6e", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "711099a9-985d-4978-a45c-88f03ac0097a", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "13954e50-cc94-48d8-a181-7e0aa8f4b105", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "88978291-3ecf-4139-a7e7-589bcce17e8d", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "785a91df-7692-43f1-9dd6-6bfcb0375e69", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": "21.1.1", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} diff --git a/examples/cernbox/nginx/mime.types b/examples/cernbox/nginx/mime.types new file mode 100644 index 0000000000..0908f64c42 --- /dev/null +++ b/examples/cernbox/nginx/mime.types @@ -0,0 +1,96 @@ + types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js mjs; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/svg+xml svg svgz; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/webp webp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + + font/woff woff; + font/woff2 woff2; + + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.oasis.opendocument.graphics odg; + application/vnd.oasis.opendocument.presentation odp; + application/vnd.oasis.opendocument.spreadsheet ods; + application/vnd.oasis.opendocument.text odt; + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx; + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx; + application/vnd.wap.wmlc wmlc; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/examples/cernbox/nginx/nginx.conf b/examples/cernbox/nginx/nginx.conf new file mode 100644 index 0000000000..7d82347969 --- /dev/null +++ b/examples/cernbox/nginx/nginx.conf @@ -0,0 +1,167 @@ + +user nginx; +worker_processes auto; + +pid /var/run/nginx.pid; + +# Load dynamic modules. See /usr/share/nginx/README.dynamic. +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + map $status $loggable { + ~^[23] 0; + default 1; + } + + access_log /var/log/nginx/access.log combined if=$loggable; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 8G; + #gzip on; + + # avoid nginx buffering all the payload, only available in > 1.7 + proxy_buffering off; + proxy_request_buffering off; + + include /etc/nginx/conf.d/*.conf; + + upstream revad { + server your.revad.org:443; + } + + server { + listen 80; + server_name localhost; + return 301 https://$host$request_uri; + } + + # https://ssl-config.mozilla.org/#server=nginx&version=1.20.1&config=intermediate&openssl=1.0.2k&guideline=5.6 + server { + listen 443 ssl http2; + server_name localhost; + + ssl_certificate your.cert.pem; + ssl_certificate_key your.key.pem; + + ssl_session_timeout 5m; + + ssl_protocols TLSv1.2; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + proxy_connect_timeout 1200; + proxy_send_timeout 1200; + proxy_read_timeout 1200; + send_timeout 1200; + proxy_buffering off; + proxy_request_buffering off; + + location ^~ /data { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ~ /s/[^/]*/download { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /ocs/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /dav/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /webdav/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /remote.php/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /preferences { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /otg { + return 204; + } + + location ^~ /app/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /ocm/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ~ ^/sciencemesh/ { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /ocm-provider { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ^~ /status.php { + proxy_pass https://revad; + proxy_set_header Host $host; + } + + location ~ ^/(js|css|icons|fonts|assets)/ { + root /var/www/web; + add_header Cache-Control "public, max-age=31536000, immutable"; + etag off; + gzip_static on; + } + + location ^~ /cernbox { + root /var/www; + add_header Cache-Control "no-cache"; + add_header Access-Control-Allow-Origin "https://idp.docker:8443" always; + etag off; + gzip_static on; + } + + location / { + root /var/www/web; + add_header Cache-Control "no-cache"; + add_header Access-Control-Allow-Origin "https://idp.docker:8443" always; + etag off; + gzip_static on; + try_files $uri /index.html; + } + +# location / { +# proxy_pass https://revad/ui; +# proxy_set_header Host $host; +# } + } +} diff --git a/examples/cernbox/users.demo.json b/examples/cernbox/users.demo.json new file mode 100644 index 0000000000..8452f49a08 --- /dev/null +++ b/examples/cernbox/users.demo.json @@ -0,0 +1,80 @@ +[ + { + "id": { + "opaque_id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "idp": "cernbox", + "type": 1 + }, + "username": "einstein", + "secret": "relativity", + "mail": "einstein@cern.ch", + "display_name": "Albert Einstein", + "groups": ["sailing-lovers", "violin-haters", "physics-lovers"], + "opaque": { + "map": { + "gid": { + "_comment": "decodes to 987", + "decoder":"plain", + "value":"OTg3" + }, + "uid":{ + "_comment": "decodes to 123", + "decoder":"plain", + "value":"MTIz" + } + } + } + }, + { + "id": { + "opaque_id": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + "idp": "cernbox", + "type": 1 + }, + "username": "marie", + "secret": "radioactivity", + "mail": "marie@cern.ch", + "display_name": "Marie Curie", + "groups": ["radium-lovers", "polonium-lovers", "physics-lovers"], + "opaque": { + "map": { + "gid": { + "_comment": "decodes to 987", + "decoder":"plain", + "value":"OTg3" + }, + "uid":{ + "_comment": "decodes to 456", + "decoder":"plain", + "value":"NDU2" + } + } + } + }, + { + "id": { + "opaque_id": "932b4540-8d16-481e-8ef4-588e4b6b151c", + "idp": "cernbox", + "type": 1 + }, + "username": "richard", + "secret": "superfluidity", + "mail": "richard@cern.ch", + "display_name": "Richard Feynman", + "groups": ["quantum-lovers", "philosophy-haters", "physics-lovers"], + "opaque": { + "map": { + "gid": { + "_comment": "decodes to 135", + "decoder":"plain", + "value":"MTM1" + }, + "uid":{ + "_comment": "decodes to 246", + "decoder":"plain", + "value":"MjQ2" + } + } + } + } +] diff --git a/examples/cernbox/web.json b/examples/cernbox/web.json new file mode 100644 index 0000000000..d712acca60 --- /dev/null +++ b/examples/cernbox/web.json @@ -0,0 +1,73 @@ +{ + "server": "https://your.nginx.org", + "theme": "https://your.nginx.org/cernbox/theme-cernbox-0.2.0/theme.json", + "version": "0.0.0", + "openIdConnect": { + "metadata_url": "https://idp.docker:8443/realms/cernbox/.well-known/openid-configuration", + "authority": "https://idp.docker:8443/realms/cernbox", + "client_id": "cernbox-oidc", + "response_type": "code", + "scope": "openid profile email" + }, + "options": { + "contextHelpers": true, + "enableAdvancedTable": true, + "cernFeatures": true, + "hoverableQuickActions": true, + "disableFeedbackLink": true, + "homeFolder": "/{{.Id}}", + "previewFileMimeTypes" : [ + "image/gif", + "image/png", + "image/jpeg" + ], + "openAppsInTab": true, + "editor": { + "autosaveEnabled": true, + "autosaveInterval": 120 + }, + "breadcrumbItemMaxLength": 30, + "routing": { + "idBased": false + }, + "upload": { + "xhr": { + "timeout": 3600000 + } + } + }, + "apps": [ + "files", + "search", + "preview", + "external", + "pdf-viewer", + "sciencemesh-app" + ], + "external_apps": [ + { + "id": "jupyter", + "path": "/cernbox/jupyter-2.0.0/main.js" + }, + { + "id": "rootjs", + "path": "/cernbox/rootjs-2.0.0/main.js" + }, + { + "id": "ifc-js", + "path": "/cernbox/ifc-js-2.0.2/main.js" + }, + { + "id": "top-bar", + "path": "/cernbox/top-bar-1.0.0/main.js" + }, + { + "id": "text-editor", + "path": "web-app-text-editor", + "config": { + "primaryExtensions": ["txt"], + "extraExtensions": ["htm", "html", "css", "js", "ts", "php", "yml", "toml", "qml", "pl", "pm", "sh", "tcsh", "zsh", "conf", "ini", "inf", "make", "cmake", "mk", "in"] + } + } + ] +} diff --git a/examples/ocm/server-1.toml b/examples/ocm/server-1.toml index a4cb399427..424f5254b6 100644 --- a/examples/ocm/server-1.toml +++ b/examples/ocm/server-1.toml @@ -166,7 +166,7 @@ driver = "ocmreceived" address = "0.0.0.0:8080" expose_recipient_display_name = true -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] ocm_prefix = "ocm" provider = "reva@cern" endpoint = "http://localhost:{{ http.services.ocm.address.port }}" diff --git a/examples/ocm/server-2.toml b/examples/ocm/server-2.toml index 7b9bf24cb3..66a00b2249 100644 --- a/examples/ocm/server-2.toml +++ b/examples/ocm/server-2.toml @@ -166,7 +166,7 @@ driver = "ocmreceived" address = "0.0.0.0:80" expose_recipient_display_name = true -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] ocm_prefix = "ocm" provider = "reva@cesnet" endpoint = "http://localhost:{{ http.services.ocm.address.port }}" diff --git a/examples/sciencemesh/custom-mime-types-demo.json b/examples/sciencemesh/custom-mime-types-demo.json new file mode 100644 index 0000000000..390947ec09 --- /dev/null +++ b/examples/sciencemesh/custom-mime-types-demo.json @@ -0,0 +1,3 @@ +{ + ".zmd": "application/compressed-markdown" +} diff --git a/examples/sciencemesh/metrics.json b/examples/sciencemesh/metrics.json new file mode 100644 index 0000000000..da85d866d9 --- /dev/null +++ b/examples/sciencemesh/metrics.json @@ -0,0 +1,5 @@ +{ + "cs3_org_sciencemesh_site_total_num_users": 0, + "cs3_org_sciencemesh_site_total_num_groups": 0, + "cs3_org_sciencemesh_site_total_amount_storage": 0 +} \ No newline at end of file diff --git a/examples/sciencemesh/providers.testnet.json b/examples/sciencemesh/providers.testnet.json new file mode 100644 index 0000000000..1960fdd63c --- /dev/null +++ b/examples/sciencemesh/providers.testnet.json @@ -0,0 +1,34 @@ +[ + { "domain": "revad1.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revad1.docker/ocm/" }, "host": "revad1.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://revad1.docker/remote.php/webdav/" }, "host": "revad1.docker" } + ] }, + { "domain": "revad2.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revad2.docker/ocm/" }, "host": "revad2.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://revad2.docker/remote.php/webdav/" }, "host": "revad2.docker" } + ] }, + { "domain": "revanextcloud1.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revanextcloud1.docker/ocm/" }, "host": "revanextcloud1.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://nc1.docker/remote.php/webdav/" }, "host": "nextcloud1.docker" } + ] }, + { "domain": "revanextcloud2.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revanextcloud2.docker/ocm/" }, "host": "revanextcloud2.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://nc2.docker/remote.php/webdav/" }, "host": "nextcloud2.docker" } + ] }, + { "domain": "revaowncloud1.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revaowncloud1.docker/ocm/" }, "host": "revaowncloud1.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://owncloud1.docker/remote.php/webdav/" }, "host": "owncloud1.docker" } + ] }, + { "domain": "revaowncloud2.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revaowncloud2.docker/ocm/" }, "host": "revaowncloud2.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://owncloud2.docker/remote.php/dav/" }, "host": "owncloud2.docker" } + ] }, + { "domain": "revacernbox1.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revacernbox1.docker/ocm/" }, "host": "revacernbox1.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://cernbox1.docker/remote.php/dav/" }, "host": "cernbox1.docker" } + ] }, + { "domain": "revacernbox2.docker", "services": [ + { "endpoint": { "type": { "name": "OCM" }, "path": "https://revacernbox2.docker/ocm/" }, "host": "revacernbox2.docker" }, + { "endpoint": { "type": { "name": "Webdav" }, "path": "https://cernbox2.docker/remote.php/dav/" }, "host": "cernbox2.docker" } + ] } +] diff --git a/examples/sciencemesh/sciencemesh-apps.toml b/examples/sciencemesh/sciencemesh-apps.toml new file mode 100644 index 0000000000..00bef3e888 --- /dev/null +++ b/examples/sciencemesh/sciencemesh-apps.toml @@ -0,0 +1,49 @@ +## Example configuration for the ScienceMesh app in ownCloud 10 / Nextcloud - Apps support +# +## This configuration file is to be used to enable Apps support in ScienceMesh. +## To contextualize this configuration for your deployment, please follow these steps: +# +# 1. In the [vars] section below, replace `your.revad.org` with your actual domain +# 2. Populate `wopi_endpoint` with your wopiserver endpoint +# 3. Define a shared secret, and match it with the secret defined in the wopiserver + + +[vars] +internal_gateway = "your.revad.org" +provider_domain = "your.revad.org" +wopi_endpoint = "http://your.wopi.org:8880" +wopi_shared_secret = "shared-secret-2" + +[log] +level = "debug" + +[shared] +gatewaysvc = "{{ vars.provider_domain }}:19000" + +[[grpc.services.appprovider]] +driver = "wopi" +custom_mime_types_json = "/etc/revad/custom-mime-types-demo.json" +mime_types = ["application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.spreadsheet", "application/vnd.oasis.opendocument.presentation", "text/rtf"] +app_provider_url = "{{ grpc.services.appprovider[0].address }}" +language = "en-GB" + +[grpc.services.appprovider.drivers.wopi] +iop_secret = "{{ vars.wopi_shared_secret }}" +wopi_url = "{{ vars.wopi_endpoint }}" +app_name = "Collabora" +app_url = "http://collabora.docker:9980" +app_int_url = "http://collabora.docker:9980" + + +# [[grpc.services.appprovider]] +# driver = "wopi" +# custom_mime_types_json = "/etc/revad/custom-mime-types-demo.json" +# mime_types = ["text/markdown", "application/compressed-markdown", "text/plain"] +# app_provider_url = "{{ grpc.services.appprovider[1].address }}" +# +# [grpc.services.appprovider.drivers.wopi] +# iop_secret = "{{ vars.wopi_shared_secret }}" +# wopi_url = "{{ vars.wopi_endpoint }}" +# app_name = "CodiMD" +# app_url = "https://codimd.docker" +# app_int_url = "https://codimd.docker" diff --git a/examples/sciencemesh/sciencemesh.toml b/examples/sciencemesh/sciencemesh.toml new file mode 100644 index 0000000000..d9fc44bfa9 --- /dev/null +++ b/examples/sciencemesh/sciencemesh.toml @@ -0,0 +1,288 @@ +## Example configuration for the ScienceMesh app in ownCloud 10 / Nextcloud +# +## To contextualize this configuration for your deployment, please follow these steps: +# +# 1. In the [vars] section below, replace `your.revad.org` with your actual domain +# 2. If you have a Kubernetes deployment with an ingress and a route: +# 2.1. Set the `external_reva_endpoint` var to your actual externally-visible route to reva +# 3. Replace `your.efss.org` with the actual endpoint of your EFSS system +# 4. Define appropriate secrets in the [vars] section: the `efss_shared_secret` must match the `oc_appconfig.configvalue` in your EFSS DB for `oc_appconfig.app_id` = `sciencemesh` +# 5. Provide appropriate SSL full chain certificate and key files in the [http] section +# +# If you want to terminate the SSL connection to reva at your reverse proxy system +# (e.g. at your Kubernetes ingress), then you can configure reva to use http instead. +# For that, you need to follow these steps: +# +# 1. Remove the `certfile` and `keyfile` entries from the [http] section +# 2. Replace the https port `443` with a port number of your choice everywhere you find it +# 3. Look for `data_server_url` and replace the `https://localhost` address as indicated + + +[vars] +internal_gateway = "your.revad.org" +provider_domain = "your.revad.org" +external_reva_endpoint = "https://your.revad.org" # append here any route if applicable +efss_sciencemesh_endpoint = "https://your.efss.org/index.php/apps/sciencemesh/" +machine_api_key = "machine-api-key" +efss_shared_secret = "shared-secret-1" + +[http] +certfile = "/etc/tls/your.revad.ssl.crt" +keyfile = "/etc/tls/your.revad.ssl.key" + +[log] +level = "debug" + +[shared] +gatewaysvc = "{{ vars.internal_gateway }}:19000" + +[grpc.services.gateway] +address = ":19000" +authregistrysvc = "{{ grpc.services.authregistry.address }}" +appregistrysvc = "{{ grpc.services.appregistry.address }}" +storageregistrysvc = "{{ grpc.services.storageregistry.address }}" +preferencessvc = "{{ grpc.services.userprovider.address }}" +userprovidersvc = "{{ grpc.services.userprovider.address }}" +usershareprovidersvc = "{{ grpc.services.usershareprovider.address }}" +ocmcoresvc = "{{ grpc.services.ocmcore.address }}" +ocmshareprovidersvc = "{{ grpc.services.ocmshareprovider.address }}" +ocminvitemanagersvc = "{{ grpc.services.ocminvitemanager.address }}" +ocmproviderauthorizersvc = "{{ grpc.services.ocmproviderauthorizer.address }}" +datagateway = "https://{{ http.services.datagateway.address }}/data" + +transfer_expires = 6 # give it a moment +commit_share_to_storage_grant = true +commit_share_to_storage_ref = true + + +[grpc.services.appregistry] +driver = "static" + +[grpc.services.appregistry.drivers.static] +mime_types = [ + {"mime_type" = "text/plain", "extension" = "txt", "name" = "Text file", "description" = "Text file", "allow_creation" = true}, + {"mime_type" = "text/markdown", "extension" = "md", "name" = "Markdown file", "description" = "Markdown file", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.text", "extension" = "odt", "name" = "OpenDocument", "description" = "OpenDocument text document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.spreadsheet", "extension" = "ods", "name" = "OpenSpreadsheet", "description" = "OpenDocument spreadsheet document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.oasis.opendocument.presentation", "extension" = "odp", "name" = "OpenPresentation", "description" = "OpenDocument presentation document", "default_app" = "Collabora", "allow_creation" = true}, + {"mime_type" = "application/vnd.jupyter", "extension" = "ipynb", "name" = "Jupyter Notebook", "description" = "Jupyter Notebook"} +] + + +### AUTH PROVIDERS ### + +[grpc.services.authregistry] +driver = "static" + +[grpc.services.authregistry.drivers.static.rules] +basic = "{{ grpc.services.authprovider[0].address }}" +machine = "{{ grpc.services.authprovider[1].address }}" +ocmshares = "{{ grpc.services.authprovider[2].address }}" + +[[grpc.services.authprovider]] +auth_manager = "nextcloud" + +[grpc.services.authprovider.auth_managers.nextcloud] +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false + +[[grpc.services.authprovider]] +auth_manager = "machine" + +[grpc.services.authprovider.auth_managers.machine] +api_key = "{{ vars.machine_api_key }}" +gateway_addr = "{{ vars.internal_gateway }}:19000" + +[[grpc.services.authprovider]] +auth_manager = "ocmshares" + + +### STORAGE PROVIDERS ### + +[grpc.services.storageregistry] +driver = "static" + +[grpc.services.storageregistry.drivers.static] +home_provider = "/home" + +[grpc.services.storageregistry.drivers.static.rules] +"/home" = {"address" = "{{ grpc.services.storageprovider[0].address }}"} +"nextcloud" = {"address" = "{{ grpc.services.storageprovider[0].address }}"} +"/ocm" = {"address" = "{{ grpc.services.storageprovider[1].address }}"} +"ocm" = {"address" = "{{ grpc.services.storageprovider[1].address }}"} + +[[grpc.services.storageprovider]] +driver = "nextcloud" +mount_id = "nextcloud" +expose_data_server = true +enable_home_creation = false +# replace https://localhost with http://localhost if reva does not serve https traffic +data_server_url = "https://localhost:{{ http.services.dataprovider[0].address.port }}/data" + +[grpc.services.storageprovider.drivers.nextcloud] +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false + +[[grpc.services.storageprovider]] +driver = "ocmoutcoming" +mount_id = "ocm" +mount_path = "/ocm" +expose_data_server = true +enable_home_creation = false +data_server_url = "{{ vars.external_reva_endpoint }}/data" + +[grpc.services.storageprovider.drivers.ocmoutcoming] +machine_secret = "{{ vars.machine_api_key }}" + + +### OTHER PROVIDERS ### + +[grpc.services.usershareprovider] +driver = "memory" + +[grpc.services.ocmcore] +driver = "nextcloud" + +[grpc.services.ocmcore.drivers.nextcloud] +host = "{{ vars.external_reva_endpoint }}" +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false + +[grpc.services.ocminvitemanager] +# TODO the driver should be "nextcloud" once it is implemented +driver = "json" +provider_domain = "{{ vars.provider_domain }}" + +[grpc.services.ocmshareprovider] +driver = "nextcloud" +provider_domain = "{{ vars.provider_domain }}" +webdav_endpoint = "{{ vars.external_reva_endpoint }}" +webdav_prefix = "{{ vars.external_reva_endpoint }}/remote.php/dav/files" +webapp_template = "{{ vars.external_reva_endpoint }}/external/sciencemesh/{{.Token}}/{relative-path-to-shared-resource}" + +[grpc.services.ocmshareprovider.drivers.nextcloud] +webdav_host = "{{ vars.external_reva_endpoint }}" +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false +mount_id = "nextcloud" + +[grpc.services.ocmproviderauthorizer] +driver = "mentix" + +[grpc.services.ocmproviderauthorizer.drivers.mentix] +url = "https://iop.sciencemesh.uni-muenster.de/iop/mentix/cs3" +verify_request_hostname = true +insecure = false +timeout = 10 +refresh = 900 + +[grpc.services.ocmproviderauthorizer.drivers.json] +# this is used by the docker-based test deployment, not in production +providers = "/etc/revad/providers.testnet.json" +verify_request_hostname = true + +[grpc.services.userprovider] +driver = "nextcloud" + +[grpc.services.userprovider.drivers.nextcloud] +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false + +[grpc.services.datatx] +txdriver = "rclone" +storagedriver = "json" +remove_transfer_on_cancel = true + +[grpc.services.datatx.txdrivers.rclone] +# rclone endpoint +endpoint = "http://rclone.docker" +# basic auth is used +auth_user = "rcloneuser" +auth_pass = "eilohtho9oTahsuongeeTh7reedahPo1Ohwi3aek" +auth_header = "x-access-token" +job_status_check_interval = 2000 +job_timeout = 120000 +storagedriver = "json" +remove_transfer_job_on_cancel = true + +[grpc.services.datatx.storagedrivers.json] +file = "" + +[grpc.services.datatx.txdrivers.rclone.storagedrivers.json] +file = "" + + +### HTTP ENDPOINTS ### + +[http.services.appprovider] +address = ":443" +insecure = true + +[http.services.datagateway] +address = ":443" + +[[http.services.dataprovider]] +driver = "nextcloud" + +[http.services.dataprovider.drivers.nextcloud] +endpoint = "{{ vars.efss_sciencemesh_endpoint }}" +shared_secret = "{{ vars.efss_shared_secret }}" +mock_http = false + +[[http.services.dataprovider]] +address = ":443" +driver = "ocmoutcoming" + +[http.services.dataprovider.drivers.ocmoutcoming] +machine_secret = "{{ vars.machine_api_key }}" + +[http.services.sciencemesh] +address = ":443" +provider_domain = "{{ vars.provider_domain }}" +mesh_directory_url = "https://sciencemesh.cesnet.cz/iop/meshdir" +ocm_mount_point = "/sciencemesh" + +[http.services.sciencemesh.smtp_credentials] +disable_auth = true +sender_mail = "sciencemesh@{{ vars.provider_domain }}" +smtp_server = "smtp.{{ vars.provider_domain }}" +smtp_port = 25 + +[http.services.wellknown.ocmprovider] +address = ":443" +ocm_prefix = "ocm" +provider = "Reva for ownCloud/Nextcloud" +endpoint = "{{ vars.external_reva_endpoint }}" +enable_webapp = true +enable_datatx = true + +[http.services.ocm] +address = ":443" +prefix = "ocm" + +[http.services.ocm.config] +host = "{{ vars.provider_domain }}" + +[http.services.ocs] +address = ":443" +prefix = "ocs" + +[http.services.ocdav] +address = ":443" + +[http.services.prometheus] +address = ":443" + +[http.services.metrics] +address = ":443" +metrics_data_driver_type = "json" +metrics_data_location = "/etc/revad/metrics.json" +metrics_record_interval = 5000 + +[http.middlewares.cors] +[http.middlewares.log] diff --git a/examples/standalone/standalone.toml b/examples/standalone/standalone.toml index 5a63df304b..e7e83ad461 100644 --- a/examples/standalone/standalone.toml +++ b/examples/standalone/standalone.toml @@ -17,6 +17,6 @@ [http.services.dataprovider] [http.services.prometheus] [http.services.ocm] -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] [http.services.ocdav] [http.services.ocs] diff --git a/examples/storage-references/gateway.toml b/examples/storage-references/gateway.toml index c57d035e51..fff03471bc 100644 --- a/examples/storage-references/gateway.toml +++ b/examples/storage-references/gateway.toml @@ -57,7 +57,7 @@ app_url = "https://your-collabora-server.org:9980" [http.services.datagateway] [http.services.prometheus] [http.services.ocm] -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] [http.services.ocdav] [http.services.ocs] diff --git a/examples/two-server-setup/gateway-1.toml b/examples/two-server-setup/gateway-1.toml index 1d3414071c..7d5d08731a 100644 --- a/examples/two-server-setup/gateway-1.toml +++ b/examples/two-server-setup/gateway-1.toml @@ -84,7 +84,7 @@ address = "0.0.0.0:19001" [http.services.datagateway] [http.services.prometheus] [http.services.ocm] -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] provider = "Reva-Server-1" endpoint = "http://localhost:19001" enable_webapp = true diff --git a/examples/two-server-setup/gateway-2.toml b/examples/two-server-setup/gateway-2.toml index 8361b3c7e8..bf3f22406d 100644 --- a/examples/two-server-setup/gateway-2.toml +++ b/examples/two-server-setup/gateway-2.toml @@ -84,7 +84,7 @@ address = "0.0.0.0:29001" [http.services.datagateway] [http.services.prometheus] [http.services.ocm] -[http.services.ocmprovider] +[http.services.wellknown.ocmprovider] provider = "Reva-Server-2" endpoint = "http://localhost:29001" enable_webapp = true diff --git a/go.mod b/go.mod index f19d0e6213..b0c6ae50ca 100644 --- a/go.mod +++ b/go.mod @@ -125,7 +125,7 @@ require ( go.opentelemetry.io/otel v1.23.1 // indirect go.opentelemetry.io/otel/trace v1.23.1 // indirect golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index a0e0b36431..e033f0747d 100644 --- a/go.sum +++ b/go.sum @@ -1731,8 +1731,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/internal/grpc/services/ocmcore/ocmcore.go b/internal/grpc/services/ocmcore/ocmcore.go index 218e3a3890..81f6f038da 100644 --- a/internal/grpc/services/ocmcore/ocmcore.go +++ b/internal/grpc/services/ocmcore/ocmcore.go @@ -18,6 +18,8 @@ package ocmcore +// This package implements the core OCM API for receiving external shares from remote EFSS systems. + import ( "context" "fmt" @@ -102,7 +104,7 @@ func (s *service) UnprotectedEndpoints() []string { return []string{"/cs3.ocm.core.v1beta1.OcmCoreAPI/CreateOCMCoreShare"} } -// CreateOCMCoreShare is called when an OCM request comes into this reva instance from. +// CreateOCMCoreShare is called when a remote OCM request comes into this reva instance. func (s *service) CreateOCMCoreShare(ctx context.Context, req *ocmcore.CreateOCMCoreShareRequest) (*ocmcore.CreateOCMCoreShareResponse, error) { if req.ShareType != ocm.ShareType_SHARE_TYPE_USER { return nil, errtypes.NotSupported("share type not supported") diff --git a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go index ac7956f6ff..a4f0d44039 100644 --- a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go +++ b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go @@ -26,9 +26,9 @@ import ( invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/ocm/client" "github.com/cs3org/reva/pkg/ocm/invite" "github.com/cs3org/reva/pkg/ocm/invite/repository/registry" "github.com/cs3org/reva/pkg/plugin" @@ -66,7 +66,7 @@ type config struct { type service struct { conf *config repo invite.Repository - ocmClient *client.OCMClient + ocmClient *ocmd.OCMClient } func (c *config) ApplyDefaults() { @@ -110,12 +110,9 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { } service := &service{ - conf: &c, - repo: repo, - ocmClient: client.New(&client.Config{ - Timeout: time.Duration(c.OCMClientTimeout) * time.Second, - Insecure: c.OCMClientInsecure, - }), + conf: &c, + repo: repo, + ocmClient: ocmd.NewClient(time.Duration(c.OCMClientTimeout)*time.Second, c.OCMClientInsecure), } return service, nil } @@ -166,7 +163,7 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite return nil, err } - remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &client.InviteAcceptedRequest{ + remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &ocmd.InviteAcceptedRequest{ Token: req.InviteToken.GetToken(), RecipientProvider: s.conf.ProviderDomain, UserID: user.GetId().GetOpaqueId(), @@ -175,19 +172,19 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite }) if err != nil { switch { - case errors.Is(err, client.ErrTokenInvalid): + case errors.Is(err, ocmd.ErrTokenInvalid): return &invitepb.ForwardInviteResponse{ Status: status.NewInvalid(ctx, "token not valid"), }, nil - case errors.Is(err, client.ErrTokenNotFound): + case errors.Is(err, ocmd.ErrTokenNotFound): return &invitepb.ForwardInviteResponse{ Status: status.NewNotFound(ctx, "token not found"), }, nil - case errors.Is(err, client.ErrUserAlreadyAccepted): + case errors.Is(err, ocmd.ErrUserAlreadyAccepted): return &invitepb.ForwardInviteResponse{ Status: status.NewAlreadyExists(ctx, err, err.Error()), }, nil - case errors.Is(err, client.ErrServiceNotTrusted): + case errors.Is(err, ocmd.ErrServiceNotTrusted): return &invitepb.ForwardInviteResponse{ Status: status.NewPermissionDenied(ctx, err, err.Error()), }, nil diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index 1de9c8427c..e26e7b02e5 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -18,6 +18,9 @@ package ocmshareprovider +// This package implements the OCM client API: it allows shares created on this Reva instance +// to be sent to a remote EFSS system via OCM. + import ( "context" "fmt" @@ -38,7 +41,6 @@ import ( "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/ocm/client" "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/ocm/share/repository/registry" "github.com/cs3org/reva/pkg/plugin" @@ -70,13 +72,13 @@ type config struct { GatewaySVC string `mapstructure:"gatewaysvc" validate:"required"` ProviderDomain string `docs:"The same domain registered in the provider authorizer" mapstructure:"provider_domain" validate:"required"` WebDAVEndpoint string `mapstructure:"webdav_endpoint" validate:"required"` - WebappTemplate string `mapstructure:"webapp_template"` + WebappTemplate string `mapstructure:"webapp_template" validate:"required"` } type service struct { conf *config repo share.Repository - client *client.OCMClient + client *ocmd.OCMClient gateway gateway.GatewayAPIClient webappTmpl *template.Template walker walker.Walker @@ -89,9 +91,6 @@ func (c *config) ApplyDefaults() { if c.ClientTimeout == 0 { c.ClientTimeout = 10 } - if c.WebappTemplate == "" { - c.WebappTemplate = "https://cernbox.cern.ch/external/sciencemesh/{{.Token}}{relative-path-to-shared-resource}" - } c.GatewaySVC = sharedconf.GetGatewaySVC(c.GatewaySVC) } @@ -119,11 +118,6 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { return nil, err } - client := client.New(&client.Config{ - Timeout: time.Duration(c.ClientTimeout) * time.Second, - Insecure: c.ClientInsecure, - }) - gateway, err := pool.GetGatewayServiceClient(pool.Endpoint(c.GatewaySVC)) if err != nil { return nil, err @@ -135,10 +129,11 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { } walker := walker.NewWalker(gateway) + ocmcl := ocmd.NewClient(time.Duration(c.ClientTimeout)*time.Second, c.ClientInsecure) service := &service{ conf: &c, repo: repo, - client: client, + client: ocmcl, gateway: gateway, webappTmpl: tpl, walker: walker, @@ -178,13 +173,14 @@ func getResourceType(info *providerpb.ResourceInfo) string { return "unknown" } -func (s *service) webdavURL(ctx context.Context, share *ocm.Share) string { - // the url is in the form of https://cernbox.cern.ch/remote.php/dav/ocm/token - p, _ := url.JoinPath(s.conf.WebDAVEndpoint, "/remote.php/dav/ocm", share.Token) +func (s *service) webdavURL(share *ocm.Share) string { + // the url is expected to be in the form https://ourserver/remote.php/dav/ocm/{ShareId}, see c.WebdavRoot in ocmprovider.go + // TODO(lopresti) take the root from http.services.wellknown.ocmprovider's config + p, _ := url.JoinPath(s.conf.WebDAVEndpoint, "/remote.php/dav/ocm", share.Id.OpaqueId) return p } -func (s *service) getWebdavProtocol(ctx context.Context, share *ocm.Share, m *ocm.AccessMethod_WebdavOptions) *ocmd.WebDAV { +func (s *service) getWebdavProtocol(share *ocm.Share, m *ocm.AccessMethod_WebdavOptions) *ocmd.WebDAV { var perms []string if m.WebdavOptions.Permissions.InitiateFileDownload { perms = append(perms, "read") @@ -195,7 +191,7 @@ func (s *service) getWebdavProtocol(ctx context.Context, share *ocm.Share, m *oc return &ocmd.WebDAV{ Permissions: perms, - URL: s.webdavURL(ctx, share), + URL: s.webdavURL(share), SharedSecret: share.Token, } } @@ -233,7 +229,7 @@ func (s *service) getDataTransferProtocol(ctx context.Context, share *ocm.Share) panic(err) } return &ocmd.Datatx{ - SourceURI: s.webdavURL(ctx, share), + SourceURI: s.webdavURL(share), Size: size, } } @@ -248,7 +244,7 @@ func (s *service) getProtocols(ctx context.Context, share *ocm.Share) ocmd.Proto for _, m := range share.AccessMethods { switch t := m.Term.(type) { case *ocm.AccessMethod_WebdavOptions: - p = append(p, s.getWebdavProtocol(ctx, share, t)) + p = append(p, s.getWebdavProtocol(share, t)) case *ocm.AccessMethod_WebappOptions: p = append(p, s.getWebappProtocol(share)) case *ocm.AccessMethod_TransferOptions: @@ -323,7 +319,7 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq }, nil } - newShareReq := &client.NewShareRequest{ + newShareReq := &ocmd.NewShareRequest{ ShareWith: formatOCMUser(req.Grantee.GetUserId()), Name: ocmshare.Name, ProviderID: ocmshare.Id.OpaqueId, @@ -348,11 +344,11 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq newShareRes, err := s.client.NewShare(ctx, ocmEndpoint, newShareReq) if err != nil { switch { - case errors.Is(err, client.ErrInvalidParameters): + case errors.Is(err, ocmd.ErrInvalidParameters): return &ocm.CreateOCMShareResponse{ Status: status.NewInvalidArg(ctx, err.Error()), }, nil - case errors.Is(err, client.ErrServiceNotTrusted): + case errors.Is(err, ocmd.ErrServiceNotTrusted): return &ocm.CreateOCMShareResponse{ Status: status.NewInvalidArg(ctx, err.Error()), }, nil diff --git a/internal/http/services/loader/loader.go b/internal/http/services/loader/loader.go index 37eabde578..437b1e8b3f 100644 --- a/internal/http/services/loader/loader.go +++ b/internal/http/services/loader/loader.go @@ -29,7 +29,6 @@ import ( _ "github.com/cs3org/reva/internal/http/services/helloworld" _ "github.com/cs3org/reva/internal/http/services/metrics" _ "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd" - _ "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmprovider" _ "github.com/cs3org/reva/internal/http/services/owncloud/ocapi" _ "github.com/cs3org/reva/internal/http/services/owncloud/ocdav" _ "github.com/cs3org/reva/internal/http/services/owncloud/ocgraph" @@ -39,5 +38,6 @@ import ( _ "github.com/cs3org/reva/internal/http/services/pprof" _ "github.com/cs3org/reva/internal/http/services/preferences" _ "github.com/cs3org/reva/internal/http/services/prometheus" + _ "github.com/cs3org/reva/internal/http/services/wellknown" // Add your own service here. ) diff --git a/pkg/ocm/client/client.go b/internal/http/services/opencloudmesh/ocmd/client.go similarity index 76% rename from pkg/ocm/client/client.go rename to internal/http/services/opencloudmesh/ocmd/client.go index faeb28d183..106f51e5ad 100644 --- a/pkg/ocm/client/client.go +++ b/internal/http/services/opencloudmesh/ocmd/client.go @@ -16,7 +16,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package client +package ocmd import ( "bytes" @@ -28,7 +28,7 @@ import ( "net/url" "time" - "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd" + "github.com/cs3org/reva/internal/http/services/wellknown" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/pkg/errors" @@ -59,62 +59,28 @@ type OCMClient struct { client *http.Client } -// Config is the configuration to be used for the OCMClient. -type Config struct { - Timeout time.Duration - Insecure bool -} - -// New returns a new OCMClient. -func New(c *Config) *OCMClient { +// NewClient returns a new OCMClient. +func NewClient(timeout time.Duration, insecure bool) *OCMClient { tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Insecure}, + TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, } return &OCMClient{ - client: &http.Client{Transport: tr}, - } -} - -// InviteAcceptedRequest contains the parameters for accepting -// an invitation. -type InviteAcceptedRequest struct { - UserID string `json:"userID"` - Email string `json:"email"` - Name string `json:"name"` - RecipientProvider string `json:"recipientProvider"` - Token string `json:"token"` -} - -// User contains the remote user's information when accepting -// an invitation. -type User struct { - UserID string `json:"userID"` - Email string `json:"email"` - Name string `json:"name"` -} - -func (r *InviteAcceptedRequest) toJSON() (io.Reader, error) { - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(r); err != nil { - return nil, err + client: &http.Client{ + Transport: tr, + Timeout: timeout, + }, } - return &b, nil } -// InviteAccepted informs the sender that the invitation was accepted to start sharing -// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post -func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *InviteAcceptedRequest) (*User, error) { - url, err := url.JoinPath(endpoint, "invite-accepted") - if err != nil { - return nil, err - } - - body, err := r.toJSON() +// Discover returns a number of properties used to discover the capabilities offered by a remote cloud storage. +// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get +func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.OcmDiscoveryData, error) { + url, err := url.JoinPath(endpoint, "/ocm-provider") if err != nil { return nil, err } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, errors.Wrap(err, "error creating request") } @@ -126,48 +92,36 @@ func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *Invi } defer resp.Body.Close() - return c.parseInviteAcceptedResponse(resp) -} - -func (c *OCMClient) parseInviteAcceptedResponse(r *http.Response) (*User, error) { - switch r.StatusCode { - case http.StatusOK: - var u User - if err := json.NewDecoder(r.Body).Decode(&u); err != nil { - return nil, errors.Wrap(err, "error decoding response body") - } - return &u, nil - case http.StatusBadRequest: - return nil, ErrTokenInvalid - case http.StatusNotFound: - return nil, ErrTokenNotFound - case http.StatusConflict: - return nil, ErrUserAlreadyAccepted - case http.StatusForbidden: - return nil, ErrServiceNotTrusted + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err } - body, err := io.ReadAll(r.Body) + var disco wellknown.OcmDiscoveryData + err = json.Unmarshal(body, &disco) if err != nil { - return nil, errors.Wrap(err, "error decoding response body") + log := appctx.GetLogger(ctx) + log.Warn().Str("sender", endpoint).Str("response", string(body)).Msg("malformed response") + return nil, errtypes.InternalError("Invalid payload on OCM discovery") } - return nil, errtypes.InternalError(string(body)) + + return &disco, nil } // NewShareRequest contains the parameters for creating a new OCM share. type NewShareRequest struct { - ShareWith string `json:"shareWith"` - Name string `json:"name"` - Description string `json:"description"` - ProviderID string `json:"providerId"` - Owner string `json:"owner"` - Sender string `json:"sender"` - OwnerDisplayName string `json:"ownerDisplayName"` - SenderDisplayName string `json:"senderDisplayName"` - ShareType string `json:"shareType"` - Expiration uint64 `json:"expiration"` - ResourceType string `json:"resourceType"` - Protocols ocmd.Protocols `json:"protocol"` + ShareWith string `json:"shareWith"` + Name string `json:"name"` + Description string `json:"description"` + ProviderID string `json:"providerId"` + Owner string `json:"owner"` + Sender string `json:"sender"` + OwnerDisplayName string `json:"ownerDisplayName"` + SenderDisplayName string `json:"senderDisplayName"` + ShareType string `json:"shareType"` + Expiration uint64 `json:"expiration"` + ResourceType string `json:"resourceType"` + Protocols Protocols `json:"protocol"` } func (r *NewShareRequest) toJSON() (io.Reader, error) { @@ -197,7 +151,7 @@ func (c *OCMClient) NewShare(ctx context.Context, endpoint string, r *NewShareRe } log := appctx.GetLogger(ctx) - log.Debug().Msgf("Sending OCM /shares POST to %s: %s", url, body) + log.Info().Str("url", url).Msgf("Sending OCM share: %s", body) req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return nil, errors.Wrap(err, "error creating request") @@ -232,34 +186,46 @@ func (c *OCMClient) parseNewShareResponse(r *http.Response) (*NewShareResponse, return nil, errtypes.InternalError(string(body)) } -// Capabilities contains a set of properties exposed by -// a remote cloud storage. -type Capabilities struct { - Enabled bool `json:"enabled"` - APIVersion string `json:"apiVersion"` - EndPoint string `json:"endPoint"` - Provider string `json:"provider"` - ResourceTypes []struct { - Name string `json:"name"` - ShareTypes []string `json:"shareTypes"` - Protocols struct { - Webdav *string `json:"webdav"` - Webapp *string `json:"webapp"` - Datatx *string `json:"datatx"` - } `json:"protocols"` - } `json:"resourceTypes"` - Capabilities []string `json:"capabilities"` +// InviteAcceptedRequest contains the parameters for accepting +// an invitation. +type InviteAcceptedRequest struct { + UserID string `json:"userID"` + Email string `json:"email"` + Name string `json:"name"` + RecipientProvider string `json:"recipientProvider"` + Token string `json:"token"` } -// Discovery returns a number of properties used to discover the capabilities offered by a remote cloud storage. -// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get -func (c *OCMClient) Discovery(ctx context.Context, endpoint string) (*Capabilities, error) { - url, err := url.JoinPath(endpoint, "shares") +// User contains the remote user's information when accepting +// an invitation. +type User struct { + UserID string `json:"userID"` + Email string `json:"email"` + Name string `json:"name"` +} + +func (r *InviteAcceptedRequest) toJSON() (io.Reader, error) { + var b bytes.Buffer + if err := json.NewEncoder(&b).Encode(r); err != nil { + return nil, err + } + return &b, nil +} + +// InviteAccepted informs the sender that the invitation was accepted to start sharing +// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post +func (c *OCMClient) InviteAccepted(ctx context.Context, endpoint string, r *InviteAcceptedRequest) (*User, error) { + url, err := url.JoinPath(endpoint, "invite-accepted") if err != nil { return nil, err } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + body, err := r.toJSON() + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return nil, errors.Wrap(err, "error creating request") } @@ -271,10 +237,30 @@ func (c *OCMClient) Discovery(ctx context.Context, endpoint string) (*Capabiliti } defer resp.Body.Close() - var cap Capabilities - if err := json.NewDecoder(resp.Body).Decode(&c); err != nil { - return nil, err + return c.parseInviteAcceptedResponse(resp) +} + +func (c *OCMClient) parseInviteAcceptedResponse(r *http.Response) (*User, error) { + switch r.StatusCode { + case http.StatusOK: + var u User + if err := json.NewDecoder(r.Body).Decode(&u); err != nil { + return nil, errors.Wrap(err, "error decoding response body") + } + return &u, nil + case http.StatusBadRequest: + return nil, ErrTokenInvalid + case http.StatusNotFound: + return nil, ErrTokenNotFound + case http.StatusConflict: + return nil, ErrUserAlreadyAccepted + case http.StatusForbidden: + return nil, ErrServiceNotTrusted } - return &cap, nil + body, err := io.ReadAll(r.Body) + if err != nil { + return nil, errors.Wrap(err, "error decoding response body") + } + return nil, errtypes.InternalError(string(body)) } diff --git a/internal/http/services/opencloudmesh/ocmd/shares.go b/internal/http/services/opencloudmesh/ocmd/shares.go index a48f040f7e..954e72ccae 100644 --- a/internal/http/services/opencloudmesh/ocmd/shares.go +++ b/internal/http/services/opencloudmesh/ocmd/shares.go @@ -21,7 +21,6 @@ package ocmd import ( "encoding/json" "fmt" - "io" "mime" "net/http" "path/filepath" @@ -37,13 +36,11 @@ import ( ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1" ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" - ocmproviderhttp "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmprovider" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/internal/http/services/reqres" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/httpclient" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/utils" "github.com/go-playground/validator/v10" @@ -282,43 +279,21 @@ func discoverOcmWebdavRoot(r *http.Request) (string, error) { log := appctx.GetLogger(ctx) log.Debug().Str("sender", r.Host).Msg("received OCM 1.0 share, attempting to discover sender endpoint") - httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, r.Host+"/ocm-provider", nil) + ocmClient := NewClient(time.Duration(10)*time.Second, true) + ocmCaps, err := ocmClient.Discover(ctx, r.Host) if err != nil { + log.Warn().Str("sender", r.Host).Err(err).Msg("failed to discover OCM sender") return "", err } - httpClient := httpclient.New( - httpclient.Timeout(time.Duration(10 * int64(time.Second))), - ) - httpRes, err := httpClient.Do(httpReq) - if err != nil { - return "", errors.Wrap(err, "failed to contact OCM sender server") - } - defer httpRes.Body.Close() - - if httpRes.StatusCode != http.StatusOK { - return "", errtypes.InternalError("Invalid HTTP response on OCM discovery") - } - body, err := io.ReadAll(httpRes.Body) - if err != nil { - return "", err - } - - var result ocmproviderhttp.DiscoveryData - err = json.Unmarshal(body, &result) - if err != nil { - log.Warn().Str("sender", r.Host).Str("response", string(body)).Msg("malformed response") - return "", errtypes.InternalError("Invalid payload on OCM discovery") - } - - for _, t := range result.ResourceTypes { + for _, t := range ocmCaps.ResourceTypes { webdavRoot, ok := t.Protocols["webdav"] if ok { // assume the first resourceType that exposes a webdav root is OK to use: as a matter of fact, // no implementation exists yet that exposes multiple resource types with different roots. - return filepath.Join(result.Endpoint, webdavRoot), nil + return filepath.Join(ocmCaps.Endpoint, webdavRoot), nil } } - log.Warn().Str("sender", r.Host).Str("response", string(body)).Msg("missing webdav root") + log.Warn().Str("sender", r.Host).Interface("response", ocmCaps).Msg("missing webdav root") return "", errtypes.NotFound("WebDAV root not found on OCM discovery") } diff --git a/internal/http/services/owncloud/ocdav/dav.go b/internal/http/services/owncloud/ocdav/dav.go index 57c662b5db..222f8461a9 100644 --- a/internal/http/services/owncloud/ocdav/dav.go +++ b/internal/http/services/owncloud/ocdav/dav.go @@ -216,24 +216,36 @@ func (h *DavHandler) Handler(s *svc) http.Handler { return } - // OC10 and Nextcloud (OCM 1.0) are using basic auth for carrying the - // shared token. - var token string - username, _, ok := r.BasicAuth() - if ok { - // OCM 1.0 - token = username - r.URL.Path = filepath.Join("/", token, r.URL.Path) - ctx = context.WithValue(ctx, ctxOCM10, true) + var token, ocmshare string + // OCM v1.1 (OCIS et al.). + bearer := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") + if bearer != "" { + // Bearer token is the shared secret, path is /{shareId}/path/to/resource. + // Here we're keeping the simpler public-share model, where the internal routing is done via the token, + // therefore we strip the shareId and reinject the token. + // TODO(lopresti) We should instead perform a lookup via shareId and leave the token just for auth. + var relPath string + token = bearer + ocmshare, relPath = router.ShiftPath(r.URL.Path) + r.URL.Path = filepath.Join("/", token, relPath) } else { - token, _ = router.ShiftPath(r.URL.Path) - ctx = context.WithValue(ctx, ctxOCM10, false) + username, _, ok := r.BasicAuth() + if ok { + // OCM v1.0 (OC10 and Nextcloud) uses basic auth for carrying the shared secret, + // and does not pass the shareId. + token = username + r.URL.Path = filepath.Join("/", token, r.URL.Path) + } else { + // compatibility for ScienceMesh: no auth, shared secret is the first element + // of the path, the shareId is not given. Leave the URL as is. + token = strings.Split(r.URL.Path, "/")[1] + } } - authRes, err := handleOCMAuth(ctx, c, token) + authRes, err := handleOCMAuth(ctx, c, ocmshare, token) switch { case err != nil: - log.Error().Err(err).Msg("error during ocm authentication") + log.Error().Err(err).Msg("error during OCM authentication") w.WriteHeader(http.StatusInternalServerError) return case authRes.Status.Code == rpc.Code_CODE_PERMISSION_DENIED: @@ -256,6 +268,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler { ctx = appctx.ContextSetToken(ctx, authRes.Token) ctx = appctx.ContextSetUser(ctx, authRes.User) ctx = metadata.AppendToOutgoingContext(ctx, appctx.TokenHeader, authRes.Token) + ctx = context.WithValue(ctx, ctxOCM, true) log.Debug().Str("token", token).Interface("user", authRes.User).Msg("OCM user authenticated") @@ -375,9 +388,10 @@ func handleSignatureAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, return c.Authenticate(ctx, &authenticateRequest) } -func handleOCMAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, token string) (*gatewayv1beta1.AuthenticateResponse, error) { +func handleOCMAuth(ctx context.Context, c gatewayv1beta1.GatewayAPIClient, ocmshare, token string) (*gatewayv1beta1.AuthenticateResponse, error) { return c.Authenticate(ctx, &gatewayv1beta1.AuthenticateRequest{ - Type: "ocmshares", - ClientId: token, + Type: "ocmshares", + ClientId: ocmshare, + ClientSecret: token, }) } diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 3631860974..71d3c33473 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -51,7 +51,7 @@ type ctxKey int const ( ctxKeyBaseURI ctxKey = iota - ctxOCM10 + ctxOCM ctxSpaceID ctxSpacePath ctxSpaceFullPath @@ -202,7 +202,7 @@ func (s *svc) Close() error { } func (s *svc) Unprotected() []string { - return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/s/", "/remote.php/dav/ocm/"} + return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/s/", "/remote.php/dav/ocm/", "/ocm-provider"} } func (s *svc) Handler() http.Handler { @@ -259,6 +259,10 @@ func (s *svc) Handler() http.Handler { http.Redirect(w, r, rURL, http.StatusMovedPermanently) return } + case "ocm-provider": + // this is to support the current/legacy discovery endpoint for OCM + http.Redirect(w, r, "/.well-known/ocm", http.StatusMovedPermanently) + return } switch head { // the old `/webdav` endpoint uses remote.php/webdav/$path diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go index 40d1b91a49..1dfd2604f5 100644 --- a/internal/http/services/owncloud/ocdav/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind.go @@ -505,25 +505,17 @@ func (s *svc) newPropRaw(key, val string) *propertyXML { } } -func supportLegacyOCMAccess(ctx context.Context, md *provider.ResourceInfo) { - ocm10, _ := ctx.Value(ctxOCM10).(bool) - if ocm10 { - // the path is something like //... - // we need to strip the token part as this - // is passed as username in the basic auth - _, md.Path = router.ShiftPath(md.Path) - } -} - func spaceHref(ctx context.Context, baseURI, fullPath string) string { // in the context of spaces, the final URL will be baseURI + //relative/path/to/space spacePath, ok := ctx.Value(ctxSpacePath).(string) if !ok { + // TODO fix panic panic("space path expected to be in the context") } relativePath := strings.TrimPrefix(fullPath, spacePath) spaceID, ok := ctx.Value(ctxSpaceID).(string) if !ok { + // TODO fix panic panic("space id expected to be in the context") } return path.Join(baseURI, spaceID, relativePath) @@ -556,10 +548,14 @@ func (s *svc) isOpenable(path string) bool { func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provider.ResourceInfo, ns string, usershares, linkshares map[string]struct{}) (*responseXML, error) { sublog := appctx.GetLogger(ctx).With().Str("ns", ns).Logger() - baseURI := ctx.Value(ctxKeyBaseURI).(string) - - supportLegacyOCMAccess(ctx, md) + md.Path = strings.TrimPrefix(md.Path, ns) + ocm, _ := ctx.Value(ctxOCM).(bool) + if ocm { + // // was injected in front of the OCM path for the routing to work, we now remove it (see internal/http/services/owncloud/ocdav/dav.go) + _, md.Path = router.ShiftPath(md.Path) + } + baseURI := ctx.Value(ctxKeyBaseURI).(string) var ref string if _, ok := ctx.Value(ctxSpaceID).(string); ok { ref = spaceHref(ctx, baseURI, md.Path) @@ -567,6 +563,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide md.Path = strings.TrimPrefix(md.Path, ns) ref = path.Join(baseURI, md.Path) } + if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { ref += "/" } diff --git a/internal/http/services/owncloud/ocs/conversions/role.go b/internal/http/services/owncloud/ocs/conversions/role.go index 3fc4c8c224..3c67b4c835 100644 --- a/internal/http/services/owncloud/ocs/conversions/role.go +++ b/internal/http/services/owncloud/ocs/conversions/role.go @@ -181,7 +181,6 @@ func NewViewerRole() *Role { Name: RoleViewer, cS3ResourcePermissions: &provider.ResourcePermissions{ GetPath: true, - GetQuota: true, InitiateFileDownload: true, ListGrants: true, ListContainer: true, @@ -200,7 +199,6 @@ func NewReaderRole() *Role { cS3ResourcePermissions: &provider.ResourcePermissions{ // read GetPath: true, - GetQuota: true, InitiateFileDownload: true, ListGrants: true, ListContainer: true, @@ -218,7 +216,6 @@ func NewEditorRole() *Role { Name: RoleEditor, cS3ResourcePermissions: &provider.ResourcePermissions{ GetPath: true, - GetQuota: true, InitiateFileDownload: true, ListGrants: true, ListContainer: true, @@ -243,7 +240,6 @@ func NewFileEditorRole() *Role { Name: RoleEditor, cS3ResourcePermissions: &provider.ResourcePermissions{ GetPath: true, - GetQuota: true, InitiateFileDownload: true, ListGrants: true, ListContainer: true, @@ -368,7 +364,6 @@ func NewLegacyRoleFromOCSPermissions(p Permissions) *Role { r.cS3ResourcePermissions.ListRecycle = true r.cS3ResourcePermissions.Stat = true r.cS3ResourcePermissions.GetPath = true - r.cS3ResourcePermissions.GetQuota = true r.cS3ResourcePermissions.InitiateFileDownload = true } if p.Contain(PermissionWrite) { @@ -424,7 +419,6 @@ func RoleFromResourcePermissions(rp *provider.ResourcePermissions) *Role { rp.ListRecycle && rp.Stat && rp.GetPath && - rp.GetQuota && rp.InitiateFileDownload { r.ocsPermissions |= PermissionRead } diff --git a/internal/http/services/opencloudmesh/ocmprovider/ocmprovider.go b/internal/http/services/wellknown/ocm.go similarity index 71% rename from internal/http/services/opencloudmesh/ocmprovider/ocmprovider.go rename to internal/http/services/wellknown/ocm.go index 539c7aefca..f8f4d53612 100644 --- a/internal/http/services/opencloudmesh/ocmprovider/ocmprovider.go +++ b/internal/http/services/wellknown/ocm.go @@ -16,27 +16,20 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package ocmprovider +package wellknown import ( - "context" "encoding/json" "net/http" "net/url" "path/filepath" "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/rhttp/global" - "github.com/cs3org/reva/pkg/utils/cfg" ) const OCMAPIVersion = "1.1.0" -func init() { - global.Register("ocmprovider", New) -} - -type config struct { +type OcmProviderConfig struct { OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"` Endpoint string `docs:"This host's full URL. If it's not configured, it is assumed OCM is not available." mapstructure:"endpoint"` Provider string `docs:"reva;A friendly name that defines this service." mapstructure:"provider"` @@ -46,7 +39,7 @@ type config struct { EnableDatatx bool `docs:"false;Whether data transfers are enabled in OCM shares." mapstructure:"enable_datatx"` } -type DiscoveryData struct { +type OcmDiscoveryData struct { Enabled bool `json:"enabled" xml:"enabled"` APIVersion string `json:"apiVersion" xml:"apiVersion"` Endpoint string `json:"endPoint" xml:"endPoint"` @@ -61,11 +54,11 @@ type resourceTypes struct { Protocols map[string]string `json:"protocols"` } -type svc struct { - data *DiscoveryData +type wkocmHandler struct { + data *OcmDiscoveryData } -func (c *config) ApplyDefaults() { +func (c *OcmProviderConfig) ApplyDefaults() { if c.OCMPrefix == "" { c.OCMPrefix = "ocm" } @@ -86,10 +79,11 @@ func (c *config) ApplyDefaults() { } } -func (c *config) prepare() *DiscoveryData { - // generates the (static) data structure to be exposed by /ocm-provider: +func (h *wkocmHandler) init(c *OcmProviderConfig) { + // generates the (static) data structure to be exposed by /.well-known/ocm: // first prepare an empty and disabled payload - d := &DiscoveryData{} + c.ApplyDefaults() + d := &OcmDiscoveryData{} d.Enabled = false d.Endpoint = "" d.APIVersion = OCMAPIVersion @@ -102,12 +96,14 @@ func (c *config) prepare() *DiscoveryData { d.Capabilities = []string{} if c.Endpoint == "" { - return d + h.data = d + return } endpointURL, err := url.Parse(c.Endpoint) if err != nil { - return d + h.data = d + return } // now prepare the enabled one @@ -129,49 +125,24 @@ func (c *config) prepare() *DiscoveryData { }} // for now we hardcode the capabilities, as this is currently only advisory d.Capabilities = []string{"/invite-accepted"} - return d + h.data = d } -// New returns a new ocmprovider object, that implements -// the OCM discovery endpoint specified in +// This handler implements the OCM discovery endpoint specified in // https://cs3org.github.io/OCM-API/docs.html?repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get -func New(ctx context.Context, m map[string]interface{}) (global.Service, error) { - var c config - if err := cfg.Decode(m, &c); err != nil { - return nil, err +func (h *wkocmHandler) Ocm(w http.ResponseWriter, r *http.Request) { + log := appctx.GetLogger(r.Context()) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + if r.UserAgent() == "Nextcloud Server Crawler" { + // Nextcloud decided to only support OCM 1.0 and 1.1, not any 1.x as per SemVer. See + // https://github.com/nextcloud/server/pull/39574#issuecomment-1679191188 + h.data.APIVersion = "1.1" + } else { + h.data.APIVersion = OCMAPIVersion + } + indented, _ := json.MarshalIndent(h.data, "", " ") + if _, err := w.Write(indented); err != nil { + log.Err(err).Msg("Error writing to ResponseWriter") } - return &svc{data: c.prepare()}, nil -} - -// Close performs cleanup. -func (s *svc) Close() error { - return nil -} - -func (s *svc) Prefix() string { - // this is hardcoded as per OCM specifications - return "/ocm-provider" -} - -func (s *svc) Unprotected() []string { - return []string{"/"} -} - -func (s *svc) Handler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log := appctx.GetLogger(r.Context()) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - if r.UserAgent() == "Nextcloud Server Crawler" { - // Nextcloud decided to only support OCM 1.0 and 1.1, not any 1.x as per SemVer. See - // https://github.com/nextcloud/server/pull/39574#issuecomment-1679191188 - s.data.APIVersion = "1.1" - } else { - s.data.APIVersion = OCMAPIVersion - } - indented, _ := json.MarshalIndent(s.data, "", " ") - if _, err := w.Write(indented); err != nil { - log.Err(err).Msg("Error writing to ResponseWriter") - } - }) } diff --git a/internal/http/services/wellknown/wellknown.go b/internal/http/services/wellknown/wellknown.go new file mode 100644 index 0000000000..5ca2c2fb0d --- /dev/null +++ b/internal/http/services/wellknown/wellknown.go @@ -0,0 +1,91 @@ +// Copyright 2018-2024 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package wellknown + +import ( + "context" + "net/http" + + "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/utils/cfg" + "github.com/go-chi/chi/v5" +) + +func init() { + global.Register("wellknown", New) +} + +type svc struct { + router chi.Router + Conf *config +} + +type config struct { + OCMProvider OcmProviderConfig `mapstructure:"ocmprovider"` +} + +// New returns a new wellknown object. +func New(ctx context.Context, m map[string]interface{}) (global.Service, error) { + var c config + if err := cfg.Decode(m, &c); err != nil { + return nil, err + } + + r := chi.NewRouter() + s := &svc{ + router: r, + Conf: &c, + } + if err := s.routerInit(); err != nil { + return nil, err + } + + return s, nil +} + +func (s *svc) routerInit() error { + wkocmHandler := new(wkocmHandler) + wkocmHandler.init(&s.Conf.OCMProvider) + s.router.Get("/ocm", wkocmHandler.Ocm) + return nil +} + +func (s *svc) Close() error { + return nil +} + +func (s *svc) Prefix() string { + return "/.well-known" +} + +func (s *svc) Unprotected() []string { + return []string{"/", "/ocm"} +} + +func (s *svc) Handler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log := appctx.GetLogger(r.Context()) + log.Debug().Str("path", r.URL.Path).Msg(".well-known routing") + + // unset raw path, otherwise chi uses it to route and then fails to match percent encoded path segments + r.URL.RawPath = "" + s.router.ServeHTTP(w, r) + }) +} diff --git a/internal/serverless/services/notifications/notifications.go b/internal/serverless/services/notifications/notifications.go index 579b8e3f34..05cb995093 100644 --- a/internal/serverless/services/notifications/notifications.go +++ b/internal/serverless/services/notifications/notifications.go @@ -45,9 +45,9 @@ import ( type config struct { NatsAddress string `docs:";The NATS server address." mapstructure:"nats_address"` - NatsToken string `docs:"The token to authenticate against the NATS server" mapstructure:"nats_token"` + NatsToken string `docs:";The token to authenticate against the NATS server" mapstructure:"nats_token"` NatsPrefix string `docs:"reva-notifications;The notifications NATS stream." mapstructure:"nats_prefix"` - HandlerConf map[string]map[string]interface{} `docs:";Settings for the different notification handlers." mapstructure:"handlers"` + HandlerConf map[string]map[string]interface{} `docs:"nil;Settings for the different notification handlers." mapstructure:"handlers"` GroupingInterval int `docs:"60;Time in seconds to group incoming notification triggers" mapstructure:"grouping_interval"` GroupingMaxSize int `docs:"100;Maximum number of notifications to group" mapstructure:"grouping_max_size"` StorageDriver string `docs:"mysql;The driver used to store notifications" mapstructure:"storage_driver"` diff --git a/netlify.toml b/netlify.toml index 1bf2fe310c..9ae80aa9cb 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,8 +1,8 @@ [build] publish = "docs/public" - command = "git submodule update -f --init --recursive && cd docs/ && npm install postcss-cli && hugo --gc --minify" + command = "git submodule update -f --init --recursive && cd docs/ && npm install postcss-cli && cd themes/docsy && npm run _mkdir:hugo-mod && cd ../../ && hugo --gc --minify" ignore = "git diff --quiet HEAD^ HEAD docs/" [build.environment] - HUGO_VERSION = "0.59.0" + HUGO_VERSION = "0.124.1" HUGO_ENV = "production" diff --git a/pkg/auth/manager/ocmshares/ocmshares.go b/pkg/auth/manager/ocmshares/ocmshares.go index c53b00a085..b347cbe817 100644 --- a/pkg/auth/manager/ocmshares/ocmshares.go +++ b/pkg/auth/manager/ocmshares/ocmshares.go @@ -82,8 +82,8 @@ func (m *manager) Configure(ml map[string]interface{}) error { return nil } -func (m *manager) Authenticate(ctx context.Context, token, _ string) (*userpb.User, map[string]*authpb.Scope, error) { - log := appctx.GetLogger(ctx).With().Str("token", token).Logger() +func (m *manager) Authenticate(ctx context.Context, ocmshare, token string) (*userpb.User, map[string]*authpb.Scope, error) { + log := appctx.GetLogger(ctx).With().Str("token", token).Str("ocmshare", ocmshare).Logger() shareRes, err := m.gw.GetOCMShareByToken(ctx, &ocm.GetOCMShareByTokenRequest{ Token: token, }) @@ -103,6 +103,12 @@ func (m *manager) Authenticate(ctx context.Context, token, _ string) (*userpb.Us return nil, nil, errtypes.InternalError(shareRes.Status.Message) } + // validate OCM share id if given (OCM v1.1) + if ocmshare != "" && shareRes.GetShare().GetId().GetOpaqueId() != ocmshare { + log.Error().Str("requested_share", ocmshare).Str("share_from_provider", shareRes.GetShare().GetId().GetOpaqueId()).Msg("mismatching ocm share id for existing secret") + return nil, nil, errtypes.InvalidCredentials("invalid shared secret") + } + // the user authenticated using the ocmshares authentication method // is the recipient of the share u := shareRes.Share.Grantee.GetUserId() diff --git a/pkg/auth/manager/oidc/oidc.go b/pkg/auth/manager/oidc/oidc.go index c9c29e7a83..bfdb9d29e1 100644 --- a/pkg/auth/manager/oidc/oidc.go +++ b/pkg/auth/manager/oidc/oidc.go @@ -185,13 +185,17 @@ func (am *mgr) isIssuerAllowed(issuer string) bool { } func (am *mgr) doUserMapping(tkn *oidc.IDToken, claims jwt.MapClaims) (string, error) { + var sub = tkn.Subject + if am.c.IDClaim != "sub" && claims[am.c.IDClaim] != nil { + sub, _ = claims[am.c.IDClaim].(string) + } if len(am.oidcUsersMapping) == 0 { - return tkn.Subject, nil + return sub, nil } // we need the custom claims for the mapping if claims[am.c.GroupClaim] == nil { // we are required to perform a user mapping but the group claim is not available - return tkn.Subject, nil + return sub, nil } mappings := make([]string, 0, len(am.oidcUsersMapping)) @@ -222,7 +226,7 @@ func (am *mgr) Authenticate(ctx context.Context, _, clientSecret string) (*user. claims, err := extractClaims(clientSecret) if err != nil { - return nil, nil, errtypes.PermissionDenied("oidc token not valid") + return nil, nil, errtypes.PermissionDenied(fmt.Sprintf("error extracting claims from oidc token: %+v", err)) } issuer, ok := extractIssuer(claims) @@ -248,7 +252,7 @@ func (am *mgr) Authenticate(ctx context.Context, _, clientSecret string) (*user. tkn, err := provider.Verifier(config).Verify(ctx, clientSecret) if err != nil { - return nil, nil, errtypes.PermissionDenied(fmt.Sprintf("oidc token not valid: %+v", err)) + return nil, nil, errtypes.PermissionDenied(fmt.Sprintf("oidc token failed verification: %+v", err)) } sub, err := am.doUserMapping(tkn, claims) diff --git a/pkg/eosclient/eosbinary/eosbinary.go b/pkg/eosclient/eosbinary/eosbinary.go index 93c0f6a8fa..ecdf8f4f51 100644 --- a/pkg/eosclient/eosbinary/eosbinary.go +++ b/pkg/eosclient/eosbinary/eosbinary.go @@ -260,16 +260,18 @@ func (c *Client) executeEOS(ctx context.Context, cmdArgs []string, auth eosclien case 0: err = nil case int(syscall.ENOENT): - err = errtypes.NotFound(errBuf.String()) + err = errtypes.NotFound("eosclient: " + errBuf.String()) case int(syscall.EPERM), int(syscall.E2BIG), int(syscall.EINVAL): - // eos reports back error code 1 (EPERM) when ? + // eos reports back error code 1 (EPERM) as a PermissionDenied error // eos reports back error code 7 (E2BIG) when the user is not allowed to read the directory // eos reports back error code 22 (EINVAL) when the user is not allowed to enter the instance errString := errBuf.String() if errString == "" { errString = fmt.Sprintf("rc = %d", exitStatus) } - err = errtypes.PermissionDenied(errString) + err = errtypes.PermissionDenied("eosclient: " + errString) + default: + err = errors.Wrap(err, fmt.Sprintf("eosclient: error while executing command: %s", errBuf.String())) } } } @@ -277,11 +279,6 @@ func (c *Client) executeEOS(ctx context.Context, cmdArgs []string, auth eosclien args := fmt.Sprintf("%s", cmd.Args) env := fmt.Sprintf("%s", cmd.Env) log.Info().Str("args", args).Str("env", env).Int("exit", exitStatus).Str("err", errBuf.String()).Msg("eos cmd") - - if err != nil && exitStatus != int(syscall.ENOENT) { // don't wrap the errtypes.NotFoundError - err = errors.Wrap(err, "eosclient: error while executing command") - } - return outBuf.String(), errBuf.String(), err } diff --git a/pkg/notification/notificationhelper/notificationhelper.go b/pkg/notification/notificationhelper/notificationhelper.go index 7fc9995be0..e17f6abc94 100644 --- a/pkg/notification/notificationhelper/notificationhelper.go +++ b/pkg/notification/notificationhelper/notificationhelper.go @@ -47,7 +47,7 @@ type Config struct { NatsAddress string `docs:";The NATS server address." mapstructure:"nats_address"` NatsToken string `docs:";The token to authenticate against the NATS server" mapstructure:"nats_token"` NatsStream string `docs:"reva-notifications;The notifications NATS stream." mapstructure:"nats_stream"` - Templates map[string]interface{} `docs:";Notification templates for the service." mapstructure:"templates"` + Templates map[string]interface{} `docs:"nil;Notification templates for the service." mapstructure:"templates"` } func defaultConfig() *Config { diff --git a/pkg/ocm/provider/authorizer/open/open.go b/pkg/ocm/provider/authorizer/open/open.go index 88516d6d3d..d0af08efb6 100644 --- a/pkg/ocm/provider/authorizer/open/open.go +++ b/pkg/ocm/provider/authorizer/open/open.go @@ -20,15 +20,17 @@ package open import ( "context" - "encoding/json" - "os" + "net/url" + "path/filepath" "strings" + "time" ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" - "github.com/cs3org/reva/pkg/errtypes" + client "github.com/cs3org/reva/internal/http/services/opencloudmesh/ocmd" "github.com/cs3org/reva/pkg/ocm/provider" "github.com/cs3org/reva/pkg/ocm/provider/authorizer/registry" "github.com/cs3org/reva/pkg/utils/cfg" + "github.com/pkg/errors" ) func init() { @@ -42,19 +44,7 @@ func New(ctx context.Context, m map[string]interface{}) (provider.Authorizer, er return nil, err } - f, err := os.ReadFile(c.Providers) - if err != nil { - return nil, err - } - providers := []*ocmprovider.ProviderInfo{} - err = json.Unmarshal(f, &providers) - if err != nil { - return nil, err - } - a := &authorizer{} - a.providers = a.getOCMProviders(providers) - return a, nil } @@ -64,9 +54,6 @@ type config struct { } func (c *config) ApplyDefaults() { - if c.Providers == "" { - c.Providers = "/etc/revad/ocm-providers.json" - } } type authorizer struct { @@ -79,7 +66,51 @@ func (a *authorizer) GetInfoByDomain(ctx context.Context, domain string) (*ocmpr return p, nil } } - return nil, errtypes.NotFound(domain) + + // not yet known: try to discover the remote OCM endpoint + ocmClient := client.NewClient(time.Duration(10)*time.Second, true) + ocmCaps, err := ocmClient.Discover(ctx, domain) + if err != nil { + return nil, errors.Wrap(err, "error probing OCM services at remote server") + } + var path string + for _, t := range ocmCaps.ResourceTypes { + webdavRoot, ok := t.Protocols["webdav"] + if ok { + // assume the first resourceType that exposes a webdav root is OK to use: as a matter of fact, + // no implementation exists yet that exposes multiple resource types with different roots. + path = filepath.Join(ocmCaps.Endpoint, webdavRoot) + } + } + host, _ := url.Parse(ocmCaps.Endpoint) + + // return a provider info record for this domain, including the OCM service + return &ocmprovider.ProviderInfo{ + Name: "ocm_" + domain, + FullName: ocmCaps.Provider, + Description: "OCM service at " + domain, + Organization: domain, + Domain: domain, + Homepage: "", + Email: "", + Properties: map[string]string{}, + Services: []*ocmprovider.Service{ + { + Endpoint: &ocmprovider.ServiceEndpoint{ + Type: &ocmprovider.ServiceType{Name: "OCM"}, + Path: ocmCaps.Endpoint, + }, + Host: host.Hostname(), + }, + { + Endpoint: &ocmprovider.ServiceEndpoint{ + Type: &ocmprovider.ServiceType{Name: "Webdav"}, + Path: path, + }, + Host: host.Hostname(), + }, + }, + }, nil } func (a *authorizer) IsProviderAllowed(ctx context.Context, provider *ocmprovider.ProviderInfo) error { @@ -89,22 +120,3 @@ func (a *authorizer) IsProviderAllowed(ctx context.Context, provider *ocmprovide func (a *authorizer) ListAllProviders(ctx context.Context) ([]*ocmprovider.ProviderInfo, error) { return a.providers, nil } - -func (a *authorizer) getOCMProviders(providers []*ocmprovider.ProviderInfo) (po []*ocmprovider.ProviderInfo) { - for _, p := range providers { - _, err := a.getOCMHost(p) - if err == nil { - po = append(po, p) - } - } - return -} - -func (a *authorizer) getOCMHost(provider *ocmprovider.ProviderInfo) (string, error) { - for _, s := range provider.Services { - if s.Endpoint.Type.Name == "OCM" { - return s.Host, nil - } - } - return "", errtypes.NotFound("OCM Host") -} diff --git a/pkg/ocm/share/sender/sender.go b/pkg/ocm/share/sender/sender.go deleted file mode 100644 index 05d6890bde..0000000000 --- a/pkg/ocm/share/sender/sender.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018-2024 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sender - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "path" - "strings" - "time" - - ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/httpclient" - "github.com/pkg/errors" -) - -const createOCMCoreShareEndpoint = "shares" - -func getOCMEndpoint(originProvider *ocmprovider.ProviderInfo) (string, error) { - for _, s := range originProvider.Services { - if s.Endpoint.Type.Name == "OCM" { - return s.Endpoint.Path, nil - } - } - return "", errors.New("json: ocm endpoint not specified for mesh provider") -} - -// Send executes the POST to the OCM shares endpoint to create the share at the -// remote site. -func Send(ctx context.Context, requestBodyMap map[string]interface{}, pi *ocmprovider.ProviderInfo) error { - requestBody, err := json.Marshal(requestBodyMap) - if err != nil { - err = errors.Wrap(err, "error marshalling request body") - return err - } - ocmEndpoint, err := getOCMEndpoint(pi) - if err != nil { - return err - } - u, err := url.Parse(ocmEndpoint) - if err != nil { - return err - } - u.Path = path.Join(u.Path, createOCMCoreShareEndpoint) - recipientURL := u.String() - - log := appctx.GetLogger(ctx) - log.Info().Msgf("in OCM Send! %s %s", recipientURL, requestBody) - - req, err := http.NewRequest(http.MethodPost, recipientURL, strings.NewReader(string(requestBody))) - if err != nil { - return errors.Wrap(err, "sender: error framing post request") - } - req.Header.Set("Content-Type", "application/json") - client := httpclient.New( - httpclient.Timeout(5 * time.Second), - ) - - resp, err := client.Do(req) - if err != nil { - err = errors.Wrap(err, "sender: error sending post request") - return err - } - - defer resp.Body.Close() - if (resp.StatusCode != http.StatusCreated) && (resp.StatusCode != http.StatusOK) { - respBody, e := io.ReadAll(resp.Body) - if e != nil { - e = errors.Wrap(e, "sender: error reading request body") - return e - } - err = errors.Wrap(fmt.Errorf("%s: %s", resp.Status, string(respBody)), "sender: error from "+ocmEndpoint) - return err - } - return nil -} diff --git a/pkg/ocm/storage/outcoming/ocm.go b/pkg/ocm/storage/outcoming/ocm.go index ed843a8b20..ac80120f7c 100644 --- a/pkg/ocm/storage/outcoming/ocm.go +++ b/pkg/ocm/storage/outcoming/ocm.go @@ -68,6 +68,7 @@ func (c *config) ApplyDefaults() { } // New creates an OCM storage driver. +// This driver exposes local resources to remote OCM users. func New(ctx context.Context, m map[string]interface{}) (storage.FS, error) { var c config if err := cfg.Decode(m, &c); err != nil { @@ -148,6 +149,9 @@ func (d *driver) shareAndRelativePathFromRef(ctx context.Context, ref *provider. } path = makeRelative(path) + log := appctx.GetLogger(ctx) + log.Info().Interface("ref", ref).Str("path", path).Str("token", token).Msg("Accessing OCM share") + share, err := d.resolveToken(ctx, token) if err != nil { return nil, "", err diff --git a/pkg/ocm/storage/received/ocm.go b/pkg/ocm/storage/received/ocm.go index bcb684b9a8..89e73ef29b 100644 --- a/pkg/ocm/storage/received/ocm.go +++ b/pkg/ocm/storage/received/ocm.go @@ -33,6 +33,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typepb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/internal/http/services/owncloud/ocdav" + "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/mime" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -62,6 +63,7 @@ func (c *config) ApplyDefaults() { } // New creates an OCM storage driver. +// This driver exposes remote OCM resources to local users. func New(ctx context.Context, m map[string]interface{}) (storage.FS, error) { var c config if err := cfg.Decode(m, &c); err != nil { @@ -153,11 +155,12 @@ func (d *driver) webdavClient(ctx context.Context, ref *provider.Reference) (*go return nil, nil, "", err } - // FIXME: it's still not clear from the OCM APIs how to use the shared secret - // will use as a token in the bearer authentication as this is the reva implementation + // use the secret as bearer authentication according to OCM v1.1+ c := gowebdav.NewClient(endpoint, "", "") c.SetHeader("Authorization", "Bearer "+secret) + log := appctx.GetLogger(ctx) + log.Info().Str("endpoint", endpoint).Interface("share", share).Str("rel", rel).Str("secret", secret).Msg("Accessing OCM share") return c, share, rel, nil } diff --git a/pkg/storage/utils/eosfs/config.go b/pkg/storage/utils/eosfs/config.go index 853d3b90df..6017edf671 100644 --- a/pkg/storage/utils/eosfs/config.go +++ b/pkg/storage/utils/eosfs/config.go @@ -29,6 +29,9 @@ type Config struct { // DefaultQuotaBytes sets the default maximum bytes available for a user DefaultQuotaBytes uint64 `mapstructure:"default_quota_bytes"` + // DefaultSecondaryQuotaBytes sets the default maximum bytes available for a secondary user + DefaultSecondaryQuotaBytes uint64 `mapstructure:"default_secondary_quota_bytes"` + // DefaultQuotaFiles sets the default maximum files available for a user DefaultQuotaFiles uint64 `mapstructure:"default_quota_files"` diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 2082de76af..c322b6b93b 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -101,6 +101,9 @@ func (c *Config) ApplyDefaults() { if c.DefaultQuotaBytes == 0 { c.DefaultQuotaBytes = 2000000000000 // 1 TB logical } + if c.DefaultSecondaryQuotaBytes == 0 { + c.DefaultSecondaryQuotaBytes = c.DefaultQuotaBytes + } if c.DefaultQuotaFiles == 0 { c.DefaultQuotaFiles = 1000000 // 1 Million } @@ -1489,12 +1492,16 @@ func (fs *eosfs) createNominalHome(ctx context.Context) error { return err } - // set quota for user + // set quota for user, depending on its type + quotaBytes := fs.conf.DefaultQuotaBytes + if u.Id.Type != userpb.UserType_USER_TYPE_PRIMARY { + quotaBytes = fs.conf.DefaultSecondaryQuotaBytes + } quotaInfo := &eosclient.SetQuotaInfo{ Username: u.Username, UID: auth.Role.UID, GID: auth.Role.GID, - MaxBytes: fs.conf.DefaultQuotaBytes, + MaxBytes: quotaBytes, MaxFiles: fs.conf.DefaultQuotaFiles, QuotaNode: fs.conf.QuotaNode, } diff --git a/pkg/storage/utils/grants/grants.go b/pkg/storage/utils/grants/grants.go index 021efe0d63..07e7262a25 100644 --- a/pkg/storage/utils/grants/grants.go +++ b/pkg/storage/utils/grants/grants.go @@ -29,7 +29,8 @@ import ( var noPermissions = &provider.ResourcePermissions{} -// GetACLPerm generates a string representation of CS3APIs' ResourcePermissions +// GetACLPerm generates a string representation of CS3APIs' ResourcePermissions, +// modeled after the EOS ACLs. // TODO(labkode): fine grained permission controls. func GetACLPerm(set *provider.ResourcePermissions) (string, error) { // resource permission is denied @@ -39,7 +40,7 @@ func GetACLPerm(set *provider.ResourcePermissions) (string, error) { var b strings.Builder - if set.Stat || set.InitiateFileDownload { + if set.Stat || set.InitiateFileDownload || set.ListGrants { b.WriteString("r") } if set.CreateContainer || set.InitiateFileUpload || set.Delete || set.Move { @@ -48,12 +49,9 @@ func GetACLPerm(set *provider.ResourcePermissions) (string, error) { if set.ListContainer || set.ListFileVersions { b.WriteString("x") } - if set.AddGrant || set.ListGrants || set.RemoveGrant { + if set.AddGrant || set.RemoveGrant { b.WriteString("m") } - if set.GetQuota { - b.WriteString("q") - } if set.Delete { b.WriteString("+d") @@ -64,10 +62,10 @@ func GetACLPerm(set *provider.ResourcePermissions) (string, error) { return b.String(), nil } -// GetGrantPermissionSet converts CS3APIs' ResourcePermissions from a string -// TODO(labkode): add more fine grained controls. +// GetGrantPermissionSet converts CS3APIs' ResourcePermissions from a string: // EOS acls are a mix of ACLs and POSIX permissions. More details can be found in -// https://github.com/cern-eos/eos/blob/master/doc/configuration/permission.rst +// https://github.com/cern-eos/eos/blob/master/doc/citrine/configuration/permission.rst. +// TODO(labkode): add more fine grained controls. func GetGrantPermissionSet(perm string) *provider.ResourcePermissions { var rp provider.ResourcePermissions // default to 0 == all denied @@ -75,6 +73,7 @@ func GetGrantPermissionSet(perm string) *provider.ResourcePermissions { rp.GetPath = true rp.Stat = true rp.InitiateFileDownload = true + rp.ListGrants = true } if strings.Contains(perm, "w") && !strings.Contains(perm, "!w") { @@ -100,14 +99,9 @@ func GetGrantPermissionSet(perm string) *provider.ResourcePermissions { if strings.Contains(perm, "m") && !strings.Contains(perm, "!m") { rp.AddGrant = true - rp.ListGrants = true rp.RemoveGrant = true } - if strings.Contains(perm, "q") && !strings.Contains(perm, "!q") { - rp.GetQuota = true - } - return &rp } diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml index c2c077b74c..9e40fe48f6 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-grpc.toml @@ -58,6 +58,7 @@ providers = "{{file_providers}}" [grpc.services.ocmshareprovider] driver = "json" webdav_endpoint = "http://{{cernboxwebdav_address}}" +webapp_template = "http://{{cernboxwebdav_address}}/external/sciencemesh/{{.Token}}{relative-path-to-shared-resource}" provider_domain = "cernbox.cern.ch" [grpc.services.ocmshareprovider.drivers.json] diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml index 3a225319ab..399b310561 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-grpc.toml @@ -38,6 +38,7 @@ providers = "{{file_providers}}" [grpc.services.ocmshareprovider] driver = "json" webdav_endpoint = "http://{{cesnethttp_address}}" +webapp_template = "http://{{cesnethttp_address}}/external/sciencemesh/{{.Token}}{relative-path-to-shared-resource}" provider_domain = "cesnet.cz" [grpc.services.ocmshareprovider.drivers.json] diff --git a/tests/integration/grpc/ocm_share_test.go b/tests/integration/grpc/ocm_share_test.go index 7851805556..fdf6056e74 100644 --- a/tests/integration/grpc/ocm_share_test.go +++ b/tests/integration/grpc/ocm_share_test.go @@ -219,6 +219,7 @@ var _ = Describe("ocm share", func() { Expect(ok).To(BeTrue()) webdavClient := gowebdav.NewClient(webdav.WebdavOptions.Uri, "", "") + webdavClient.SetHeader("Authorization", "Bearer "+webdav.WebdavOptions.SharedSecret) d, err := webdavClient.Read(".") Expect(err).ToNot(HaveOccurred()) Expect(d).To(Equal([]byte("test"))) @@ -299,6 +300,7 @@ var _ = Describe("ocm share", func() { Expect(ok).To(BeTrue()) webdavClient := gowebdav.NewClient(webdav.WebdavOptions.Uri, "", "") + webdavClient.SetHeader("Authorization", "Bearer "+webdav.WebdavOptions.SharedSecret) data := []byte("new-content") webdavClient.SetHeader(ocdav.HeaderUploadLength, strconv.Itoa(len(data))) err = webdavClient.Write(".", data, 0) @@ -394,7 +396,7 @@ var _ = Describe("ocm share", func() { Expect(ok).To(BeTrue()) webdavClient := gowebdav.NewClient(webdav.WebdavOptions.Uri, "", "") - + webdavClient.SetHeader("Authorization", "Bearer "+webdav.WebdavOptions.SharedSecret) ok, err = helpers.SameContentWebDAV(webdavClient, fileToShare.Path, structure) Expect(err).ToNot(HaveOccurred()) Expect(ok).To(BeTrue()) @@ -499,6 +501,7 @@ var _ = Describe("ocm share", func() { webdavClient := gowebdav.NewClient(webdav.WebdavOptions.Uri, "", "") data := []byte("new-content") webdavClient.SetHeader(ocdav.HeaderUploadLength, strconv.Itoa(len(data))) + webdavClient.SetHeader("Authorization", "Bearer "+webdav.WebdavOptions.SharedSecret) err = webdavClient.Write("new-file", data, 0) Expect(err).ToNot(HaveOccurred())