diff --git a/docs/services/etherpad/How it works.md b/docs/services/etherpad/How it works.md new file mode 100644 index 0000000..42f46c3 --- /dev/null +++ b/docs/services/etherpad/How it works.md @@ -0,0 +1,72 @@ +# How it works + +## Configuration +- ETHERPAD_COOKIE__EXPIRES_SECONDS - time in seconds after which a session expires +- ETHERPAD_ETHERPAD_COOKIE_RELEASE_THRESHOLD - time in seconds after which a session is not returned to the user +- ETHERPAD_API_KEY - api key used for authentication of schulcloud server requests +- ETHERPAD_URI - Used as base path for api client in nest +- ETHERPAD__PAD_PATH - Used in legacy client to set path on cookie. +- ETHERPAD__PAD_URI - Used for collab editor and lesson to build return url, Used in legacy client to build url +- ETHERPAD__NEW_DOMAIN - Used in legacy client to validate url +- ETHERPAD__OLD_DOMAIN - Used in legacy client to validate url +- ETHERPAD_API_PATH - Only used in tests. +- ETHERPAD_BASE_URL - Not used. + +## Creating and Opening an Etherpad Element on a Column Board + +![Requests of etherpad creation](how_it_works.png) + +### Creating an Etherpad Element +1. The user initiates the process by creating an Etherpad element on a column board. +2. Vue then sends a request to the board module in the Schulcloud server for a new Etherpad element. +3. The server responds by returning an Etherpad element which is then displayed on the board. + +### Interacting with the Etherpad Element +1. When the user clicks on the Etherpad element, the Vue client sends a request to the collaborative text editor module in the Schulcloud server for a new Etherpad. +2. The Schulcloud server is capable of creating Etherpads based on parent types. The parent type is further used for handling authorisation of the etherpad. Currently, the only parent type is a column board element. + +### Grouping and Creating Etherpads +1. The Schulcloud server first creates a group that clusters several Etherpads together. Each group shares a session and a new group is created for every column board element. This requires sending a request to the Etherpad server. +2. Once the group is created, the server can send a request to create an Etherpad within that group. + +### Session Creation and Cookie Setting +1. For session creation, the server first needs to request an Etherpad author and then the session for that author. +2. With that session, the server can set a session cookie in the client's browser and return the Etherpad URL to Vue. + +### Opening the Etherpad +In the final step, Vue opens the Etherpad URL in a new browser tab, enabling the user to interact directly with the Etherpad. It's important to note that the Etherpad client interface displayed to the user is served by the Etherpad server, and as such, it is not a part of our own codebase. + +## Etherpad Adapter +For the communication with the Etherpad server, the Schulcloud server uses an adapter. This adapter is responsible for handling all requests to the Etherpad server and ensuring that the correct data is sent and received. This adapter uses a client that is generated by open api generator. Client generation can be triggered with `generate-client:etherpad` and should be executed after update of etherpad server. + +## Authentication +The authentication process in our system works via a token. This token is sent with each request as a parameter to ensure secure communication. + +The token is defined by the `ETHERPAD_API_KEY` environment variable. This variable holds the key used for authentication, ensuring that only authorized requests are processed. + +In the Schulcloud server, this API key is passed to the etherpad adapter on its initialization in the collaborative text editor module. + +## Session managment + +### Etherpad Session Creation and Expiration +Each interaction with an Etherpad element initiates the creation of a new session. The lifespan of this session is determined by the `ETHERPAD_COOKIE__EXPIRES_SECONDS` environment variable. Once this time period elapses, the user loses access to the pad. This loss of access is indicated by the display of a non-translated English message: "You do not have permission to access this pad." However, even after the session expires, users can still view the pad content as long as they do not interact with it. + +### Session Reuse +Upon subsequent interactions with the Etherpad element, an existing session for that element may be reused. Whether an old session is reused or a new one is created depends on the environment variable settings. The `ETHERPAD_COOKIE_RELEASE_THRESHOLD` variable determines the remaining validity period of a session for it to be delivered to the user. In the current production environment, both `ETHERPAD_COOKIE_RELEASE_THRESHOLD` and `ETHERPAD_COOKIE__EXPIRES_SECONDS` are set to the same value of 2 hours. This configuration results in the creation of a new session for each interaction with an Etherpad element. + +### Session Renewal +Currently there is no automatic session renewal upon interaction. Etherpad provides the `COOKIE_SESSION_REFRESH_INTERVAL` configuration variable, which specifies the time period after which a user's session is automatically renewed in an open tab. However, this variable is currently unset, and the default value of 1 day has no effect because it exceeds the `ETHERPAD_COOKIE__EXPIRES_SECONDS` value. Therefore, sessions are not automatically renewed. + +### Session Removal +When a user logs out of Schulcloud, all sessions are terminated, and the user loses access to the pads upon interaction. However, Etherpad sessions are not automatically removed upon user auto-logout in Schulcloud. As long as `ETHERPAD_COOKIE__EXPIRES_SECONDS` and `JWT_TIMEOUT_SECONDS` are set to the same value, all sessions should theoretically become invalid after auto-logout. + +### Cookie Management +A cookie named sessionID is stored for each session. These cookies are not programmatically removed after the `ETHERPAD_COOKIE__EXPIRES_SECONDS` period or upon Schulcloud logout. + +### Legacy Topics + +Same session behavior also applies to legacy code. Env vars are used by both implementations. + +In contrast to the nest code in legacy code a session is created for every course and stored as a cookie for every etherpad. + + diff --git a/docs/services/etherpad/Local setup.md b/docs/services/etherpad/Local setup.md new file mode 100644 index 0000000..9fda7b3 --- /dev/null +++ b/docs/services/etherpad/Local setup.md @@ -0,0 +1,122 @@ +# Local Setup + +## Running the Etherpad server + +To run the Etherpad service for the local development and resting purposes you can follow the below steps: + +1. Create a new dir that will contain all the needed files that we'll want to use when running the Etherpad service. +Create a directory called sc-etherpad and then enter it, in Unix-like systems you can use the following command: + + `mkdir sc-etherpad && cd sc-etherpad` + +2. Create a new file called APIKEY.txt in it, with the following content: + + `381d67e6347d235ac9446da3ea10a82efd6f8ae09fa2e90efeda80f82feeb4fd` + + We'll use this file to set a fixed Etherpad's API key on the Etherpad server's start. + +3. Create also a file called settings.env with the following content: + + ``` + REQUIRE_SESSION="true" + PAD_OPTIONS_SHOW_CHAT="true" + DISABLE_IP_LOGGING="true" + DEFAULT_PAD_TEXT="Schreib etwas!\n\nDieses Etherpad wird synchronisiert, während du tippst, so dass alle Betrachter jederzeit den selben Text sehen. So könnt ihr auf einfache Weise gemeinsam an Dokumenten arbeiten." + DB_TYPE=mongodb + DB_URL=mongodb://host.docker.internal:27017/etherpad + ``` + We'll use this file to provide all the needed environment variables to the Etherpad's server. + + Please note the last line, that contains the MongoDB connection string: + + `DB_URL=mongodb://host.docker.internal:27017/etherpad` + + Here we're using the host.docker.internal hostname which should make it possible for the Etherpad's container to connect to the host's local network and should work out of the box e.g. on macOS. But please modify it accordingly to your needs and your Docker's network configuration. An alternative configuaration would be to use `DB_URL=mongodb://localhost:27017/etherpad` and than add `--network="host"` to the following docker run command. + +4. Next, start the Etherpad's container: + ``` + docker run -d \ + -p 9001:9001 \ + --env-file ./settings.env \ + -v ./APIKEY.txt:/opt/etherpad-lite/APIKEY.txt \ + --name sc-etherpad \ + docker.io/etherpad/etherpad:2.0.0 + ``` + Please note we're using the docker.io/etherpad/etherpad:2.0.0 image in the command above which might be not the one that is being used anytime in the future when you read this article. To make sure you're using the current version (the one that is currently being used in the SchulCloud platform), please refer to the https://github.com/hpi-schul-cloud/dof_app_deploy/blob/main/ansible/group_vars/infra/dof_etherpad.yml file. + +Now we should have the Etherpad service running locally on port 9001, we can verify it's working properly e.g. by fetching the current Etherpad's API version: + +``` +~ curl -v http://127.0.0.1:9001/api +* Trying 127.0.0.1:9001... +* Connected to 127.0.0.1 (127.0.0.1) port 9001 +> GET /api HTTP/1.1 +> Host: 127.0.0.1:9001 +> User-Agent: curl/8.4.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< X-Powered-By: Express +< X-UA-Compatible: IE=Edge,chrome=1 +< Referrer-Policy: same-origin +< Content-Type: application/json; charset=utf-8 +< Content-Length: 26 +< ETag: W/"1a-2HmNLqF3wPt+KQRlEmGwH4O+xu4" +< Date: Fri, 29 Mar 2024 13:27:00 GMT +< Connection: keep-alive +< Keep-Alive: timeout=5 +< +* Connection #0 to host 127.0.0.1 left intact +{"currentVersion":"1.3.0"} +``` + +We can also verify that the API key has been set successfully, let's use the example API call from the Etherpad's documentation ( https://etherpad.org/doc/v2.0.0/#_example_1 ): + +``` +~ curl -v http://127.0.0.1:9001/api/1/createAuthorIfNotExistsFor\?apikey\=381d67e6347d235ac9446da3ea10a82efd6f8ae09fa2e90efeda80f82feeb4fd\&name\=Michael\&authorMapper\=7 +* Trying 127.0.0.1:9001... +* Connected to 127.0.0.1 (127.0.0.1) port 9001 +> GET /api/1/createAuthorIfNotExistsFor?apikey=381d67e6347d235ac9446da3ea10a82efd6f8ae09fa2e90efeda80f82feeb4fd&name=Michael&authorMapper=7 HTTP/1.1 +> Host: 127.0.0.1:9001 +> User-Agent: curl/8.4.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< X-Powered-By: Express +< X-UA-Compatible: IE=Edge,chrome=1 +< Referrer-Policy: same-origin +< Content-Type: application/json; charset=utf-8 +< Content-Length: 66 +< ETag: W/"42-bg92QA1xRFO6QmkNRbNXhfsFBUM" +< Date: Fri, 29 Mar 2024 13:40:05 GMT +< Connection: keep-alive +< Keep-Alive: timeout=5 +< +* Connection #0 to host 127.0.0.1 left intact +{"code":0,"message":"ok","data":{"authorID":"a.7BgkAuzbHXR1G8Cv"}} +``` + +In case of an unsuccessful result (e.g. improperly set or invalid API key) we would receive the following response: +``` +~ curl -v http://127.0.0.1:9001/api/1/createAuthorIfNotExistsFor\?apikey\=secret\&name\=Michael\&authorMapper\=7 +* Trying 127.0.0.1:9001... +* Connected to 127.0.0.1 (127.0.0.1) port 9001 +> GET /api/1/createAuthorIfNotExistsFor?apikey=secret&name=Michael&authorMapper=7 HTTP/1.1 +> Host: 127.0.0.1:9001 +> User-Agent: curl/8.4.0 +> Accept: */* +> +< HTTP/1.1 401 Unauthorized +< X-Powered-By: Express +< X-UA-Compatible: IE=Edge,chrome=1 +< Referrer-Policy: same-origin +< Content-Type: application/json; charset=utf-8 +< Content-Length: 54 +< ETag: W/"36-dbJd0O+vdNi3zPpwRXE+1EGLTho" +< Date: Fri, 29 Mar 2024 13:39:44 GMT +< Connection: keep-alive +< Keep-Alive: timeout=5 +< +* Connection #0 to host 127.0.0.1 left intact +{"code":4,"message":"no or wrong API Key","data":null} +``` \ No newline at end of file diff --git a/docs/services/etherpad/how_it_works.png b/docs/services/etherpad/how_it_works.png new file mode 100644 index 0000000..cbf71e8 Binary files /dev/null and b/docs/services/etherpad/how_it_works.png differ