mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
Merge branch 'main' into versions/4.0.0
This commit is contained in:
41
documentation/README.md
Normal file
41
documentation/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Documentation
|
||||
|
||||
Welcome to the Community Solid Server!
|
||||
Here we will cover many aspects of the server,
|
||||
such as how to propose changes,
|
||||
what the architecture looks like,
|
||||
and how to use many of the features the server provides.
|
||||
|
||||
The documentation here is still incomplete both in content and structure, so feel free to open
|
||||
a [discussion](https://github.com/CommunitySolidServer/CommunitySolidServer/discussions) about things you want to see added.
|
||||
While we try to update this documentation together with updates in the code,
|
||||
it is always possible we miss something,
|
||||
so please report it if you find incorrect information or links that no longer work.
|
||||
|
||||
An introductory tutorial that gives a quick overview of the Solid and CSS basics can be found
|
||||
[here](https://github.com/KNowledgeOnWebScale/solid-linked-data-workshops-hands-on-exercises/blob/main/css-tutorial.md).
|
||||
This is a good way to get started with the server and its setup.
|
||||
|
||||
If you want to know what is new in the latest version,
|
||||
you can check out the [release notes](https://github.com/CommunitySolidServer/CommunitySolidServer/blob/main/RELEASE_NOTES.md)
|
||||
for a high level overview and information on how to migrate your configuration to the next version.
|
||||
A list that includes all minor changes can be found in
|
||||
the [changelog](https://github.com/CommunitySolidServer/CommunitySolidServer/blob/main/CHANGELOG.md)
|
||||
|
||||
## Using the server
|
||||
|
||||
* [Basic example HTTP requests](example-requests.md)
|
||||
* [How to use the Identity Provider](identity-provider.md)
|
||||
|
||||
## What the internals look like
|
||||
|
||||
* [How the server uses dependency injection](dependency-injection.md)
|
||||
* [What the architecture looks like](architecture.md)
|
||||
|
||||
## Making changes
|
||||
|
||||
* [How to make changes to the repository](making-changes.md)
|
||||
|
||||
For core developers with push access only:
|
||||
|
||||
* [How to release a new version](release.md)
|
||||
73
documentation/architecture.md
Normal file
73
documentation/architecture.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Architecture overview
|
||||
|
||||
The initial architecture document the project was started from can be found [here](https://rubenverborgh.github.io/solid-server-architecture/solid-architecture-v1-3-0.pdf).
|
||||
Many things have been added since the original inception of the project,
|
||||
but the core ideas within that document are still valid.
|
||||
|
||||
As can be seen from the architecture, an important idea is the modularity of all components.
|
||||
No actual implementations are defined there, only their interfaces.
|
||||
Making all the components independent of each other in such a way provides us with an enormous flexibility:
|
||||
they can all be replaced by a different implementation, without impacting anything else.
|
||||
This is how we can provide many different configurations for the server,
|
||||
and why it is impossible to provide ready solutions for all possible combinations.
|
||||
|
||||
## Handlers
|
||||
A very important building block that gets reused in many places is the `AsyncHandler`.
|
||||
The idea is that a handler has 2 important functions.
|
||||
`canHandle` determines if this class is capable of correctly handling the request,
|
||||
and throws an error if it can not.
|
||||
For example, a class that converts JSON-LD to turtle can handle all requests containing JSON-LD data,
|
||||
but does not know what to do with a request that contains a JPEG.
|
||||
The second function is `handle` where the class executes on the input data and returns the result.
|
||||
If an error gets thrown here it means there is an issue with the input.
|
||||
For example, if the input data claims to be JSON-LD but is actually not.
|
||||
|
||||
The power of using this interface really shines when using certain utility classes.
|
||||
The one we use the most is the `WaterfallHandler`,
|
||||
which takes as input a list of handlers of the same type.
|
||||
The input and output of a `WaterfallHandler` is the same as those of its inputs,
|
||||
meaning it can be used in the same places.
|
||||
When doing a `canHandle` call, it will iterate over all its input handlers
|
||||
to find the first one where the `canHandle` call succeeds,
|
||||
and when calling `handle` it will return the result of that specific handler.
|
||||
This allows us to chain together many handlers that each have their specific niche,
|
||||
such as handler that each support a specific HTTP method (GET/PUT/POST/etc.),
|
||||
or handlers that only take requests targeting a specific subset of URLs.
|
||||
To the parent class it will look like it has a handler that supports all methods,
|
||||
while in practice it will be a `WaterfallHandler` containing all these separate handlers.
|
||||
|
||||
Some other utility classes are the `ParallelHandler` that runs all handlers simultaneously,
|
||||
and the `SequenceHandler` that runs all of them one after the other.
|
||||
Since multiple handlers are executed here, these only work for handlers that have no output.
|
||||
|
||||
## Streams
|
||||
Almost all data is handled in a streaming fashion.
|
||||
This allows us to work with very large resources without having to fully load them in memory,
|
||||
a client could be reading data that is being returned by the server while the server is still reading the file.
|
||||
Internally this means we are mostly handling data as `Readable` objects.
|
||||
We actually use `Guarded<Readable>` which is an internal format we created to help us with error handling.
|
||||
Such streams can be created using utility functions such as `guardStream` and `guardedStreamFrom`.
|
||||
Similarly, we have a `pipeSafely` to pipe streams in such a way that also helps with errors.
|
||||
|
||||
## Example request
|
||||
In this section we will give a high level overview of all the components
|
||||
a request passes through when it enters the server.
|
||||
This is specifically an LDP request, e.g. a POST request to create a new resource.
|
||||
|
||||
1. The correct `HttpHandler` gets found, responsible for LDP requests.
|
||||
2. The HTTP request gets parsed into a manageable format, both body and metadata such as headers.
|
||||
3. The identification credentials of the request, if any, are extracted and parsed to authenticate the calling agent.
|
||||
4. The request gets authorized or rejected, based on the credentials from step 3
|
||||
and the authorization rules of the target resource.
|
||||
5. Based on the HTTP method, the corresponding method from the `ResourceStore` gets called,
|
||||
which in the case of a POST request will return the location of the newly created error.
|
||||
6. The returned data and metadata get converted to an HTTP response and sent back in the `ResponseWriter`.
|
||||
|
||||
In case any of the steps above error, an error will be thrown.
|
||||
The `ErrorHandler` will convert the error to an HTTP response to be returned.
|
||||
|
||||
Below are sections that go deeper into the specific steps.
|
||||
Not all steps are covered yet and will be added in the future.
|
||||
|
||||
[How authentication and authorization work](authorization.md)
|
||||
[What the `ResourceStore` looks like](resource-store.md)
|
||||
56
documentation/authorization.md
Normal file
56
documentation/authorization.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Authorization
|
||||
|
||||
Authorization is usually handled by the `AuthorizingHttpHandler`,
|
||||
and goes in the following steps:
|
||||
|
||||
1. Identify the credentials of the agent making the call.
|
||||
2. Extract which access modes are needed for the request.
|
||||
3. Reading the permissions the agent has.
|
||||
4. Compare the above results to see if the request is allowed.
|
||||
|
||||
## Authentication
|
||||
There are multiple `CredentialsExtractor`s that each determine identity in a different way.
|
||||
Potentially multiple extractors can apply,
|
||||
making a requesting agent have multiple credentials.
|
||||
The `DPoPWebIdExtractor` is most relevant for the [Solid-OIDC specification](https://solid.github.io/solid-oidc/),
|
||||
as it parses the access token generated by a Solid Identity Provider.
|
||||
Besides that there are always the public credentials, which everyone has.
|
||||
There are also some debug extractors that can be used to simulate credentials,
|
||||
which can be enabled as different options through the `config/ldp/authentication` imports.
|
||||
|
||||
If successful, a `CredentialsExtractor` will return a key/value map
|
||||
linking the type of credentials to their specific values.
|
||||
|
||||
## Modes extraction
|
||||
Access modes are a predefined list of `read`, `write`, `append`, `create` and `delete`.
|
||||
The `ModesExtractor`s determine which modes will be necessary,
|
||||
based on the request contents.
|
||||
The `MethodModesExtractor` determines modes based on the HTTP method.
|
||||
A GET request will always need the `read` mode for example.
|
||||
Specifically for PATCH requests there are extractors for each supported PATCH type,
|
||||
such as the `N3PatchModesExtractor`,
|
||||
which parses the N3 Patch body to know if it will add new data or only delete data.
|
||||
|
||||
## Permission reading
|
||||
`PermissionReaders` take the input of the above to determine which permissions are available for which credentials.
|
||||
The modes from the previous step are not yet needed,
|
||||
but can be used as optimization as we only need to know if we have permission on those modes.
|
||||
Each reader can potentially return a potential answer if it only checks specific cases.
|
||||
Those results then get combined in the `UnionPermissionReader`.
|
||||
In the default configuration there are currently 4 relevant permission readers that get combined:
|
||||
|
||||
1. `PathBasedReader` rejects all permissions for certain paths, to prevent access to internal data.
|
||||
2. `OwnerPermissionReader` grants control permissions to agents that are trying to access data in a pod that they own.
|
||||
3. `AuxiliaryReader` handles all permissions for auxiliary resources by requesting those of the subject resource if necessary.
|
||||
4. `WebAclReader` reads out the relevant `.acl` resource to read out the defined permissions.
|
||||
|
||||
All of the above is if you have WebACL enabled.
|
||||
It is also possible to always grant all permissions for debugging reasons
|
||||
by changing the authorization import to `config/ldp/authorization/allow-all.json`.
|
||||
|
||||
## Authorization
|
||||
All the results of the previous steps then get combined to either allow or reject a request.
|
||||
If no permissions are found for a requested mode,
|
||||
or they are explicitly forbidden,
|
||||
a 401/403 will be returned,
|
||||
depending on if the agent was logged in or not.
|
||||
54
documentation/dependency-injection.md
Normal file
54
documentation/dependency-injection.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Dependency injection
|
||||
|
||||
The community server uses the _dependency injection_ framework
|
||||
[Components.js](https://github.com/LinkedSoftwareDependencies/Components.js/)
|
||||
to link all class instances together,
|
||||
and uses [Components-Generator.js](https://github.com/LinkedSoftwareDependencies/Components-Generator.js)
|
||||
to automatically generate the necessary description configurations of all classes.
|
||||
This framework allows us to configure our components in a JSON file.
|
||||
The advantage of this is that changing the configuration of components does not require any changes to the code,
|
||||
as one can just change the default configuration file, or provide a custom configuration file.
|
||||
|
||||
More information can be found in the Components.js [documentation](https://componentsjs.readthedocs.io/),
|
||||
but a summarized overview can be found below.
|
||||
|
||||
## Component files
|
||||
Components.js requires a component file for every class you might want to instantiate.
|
||||
Fortunately those get generated automatically by Components-Generator.js.
|
||||
Calling `npm run build` will call the generator and generate those JSON-LD files in the `dist` folder.
|
||||
The generator uses the `index.ts`, so new classes always have to be added there
|
||||
or they will not get a component file.
|
||||
|
||||
## Configuration files
|
||||
Configuration files are how we tell Components.js which classes to instantiate and link together.
|
||||
All the community server configurations can be found in
|
||||
the [`config` folder](https://github.com/CommunitySolidServer/CommunitySolidServer/tree/main/config/).
|
||||
That folder also contains information about how different pre-defined configurations can be used.
|
||||
|
||||
A single component in such a configuration file might look as follows:
|
||||
```json
|
||||
{
|
||||
"comment": "Storage used for account management.",
|
||||
"@id": "urn:solid-server:default:AccountStorage",
|
||||
"@type": "JsonResourceStorage",
|
||||
"source": { "@id": "urn:solid-server:default:ResourceStore" },
|
||||
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||
"container": "/.internal/accounts/"
|
||||
}
|
||||
```
|
||||
|
||||
With the corresponding constructor of the `JsonResourceStorage` class:
|
||||
```ts
|
||||
public constructor(source: ResourceStore, baseUrl: string, container: string)
|
||||
```
|
||||
|
||||
The important elements here are the following:
|
||||
* `"comment"`: _(optional)_ A description of this component.
|
||||
* `"@id"`: _(optional)_ A unique identifier of this component, which allows it to be used as parameter values in different places.
|
||||
* `"@type"`: The class name of the component. This must be a TypeScript class name that is exported via `index.ts`.
|
||||
|
||||
As you can see from the constructor, the other fields are direct mappings from the constructor parameters.
|
||||
`source` references another object, which we refer to using its identifier `urn:solid-server:default:ResourceStore`.
|
||||
`baseUrl` is just a string, but here we use a variable that was set before calling Components.js
|
||||
which is why it also references an `@id`.
|
||||
These variables are set when starting up the server, based on the command line parameters.
|
||||
86
documentation/example-requests.md
Normal file
86
documentation/example-requests.md
Normal file
@@ -0,0 +1,86 @@
|
||||
## Interacting with the server
|
||||
|
||||
### `PUT`: Creating resources for a given URL
|
||||
|
||||
Create a plain text file:
|
||||
```shell
|
||||
curl -X PUT -H "Content-Type: text/plain" \
|
||||
-d "abc" \
|
||||
http://localhost:3000/myfile.txt
|
||||
```
|
||||
|
||||
Create a turtle file:
|
||||
```shell
|
||||
curl -X PUT -H "Content-Type: text/turtle" \
|
||||
-d "<ex:s> <ex:p> <ex:o>." \
|
||||
http://localhost:3000/myfile.ttl
|
||||
```
|
||||
|
||||
### `POST`: Creating resources at a generated URL
|
||||
|
||||
Create a plain text file:
|
||||
```shell
|
||||
curl -X POST -H "Content-Type: text/plain" \
|
||||
-d "abc" \
|
||||
http://localhost:3000/
|
||||
```
|
||||
|
||||
Create a turtle file:
|
||||
```shell
|
||||
curl -X POST -H "Content-Type: text/turtle" \
|
||||
-d "<ex:s> <ex:p> <ex:o>." \
|
||||
http://localhost:3000/
|
||||
```
|
||||
|
||||
The response's `Location` header will contain the URL of the created resource.
|
||||
|
||||
### `GET`: Retrieving resources
|
||||
|
||||
Retrieve a plain text file:
|
||||
```shell
|
||||
curl -H "Accept: text/plain" \
|
||||
http://localhost:3000/myfile.txt
|
||||
```
|
||||
|
||||
Retrieve a turtle file:
|
||||
```shell
|
||||
curl -H "Accept: text/turtle" \
|
||||
http://localhost:3000/myfile.ttl
|
||||
```
|
||||
|
||||
Retrieve a turtle file in a different serialization:
|
||||
```shell
|
||||
curl -H "Accept: application/ld+json" \
|
||||
http://localhost:3000/myfile.ttl
|
||||
```
|
||||
|
||||
### `DELETE`: Deleting resources
|
||||
|
||||
```shell
|
||||
curl -X DELETE http://localhost:3000/myfile.txt
|
||||
```
|
||||
|
||||
### `PATCH`: Modifying resources
|
||||
|
||||
Currently, only patches over RDF resources are supported using [SPARQL Update](https://www.w3.org/TR/sparql11-update/)
|
||||
queries without `WHERE` clause.
|
||||
|
||||
```shell
|
||||
curl -X PATCH -H "Content-Type: application/sparql-update" \
|
||||
-d "INSERT DATA { <ex:s2> <ex:p2> <ex:o2> }" \
|
||||
http://localhost:3000/myfile.ttl
|
||||
```
|
||||
|
||||
### `HEAD`: Retrieve resources headers
|
||||
|
||||
```shell
|
||||
curl -I -H "Accept: text/plain" \
|
||||
http://localhost:3000/myfile.txt
|
||||
```
|
||||
|
||||
### `OPTIONS`: Retrieve resources communication options
|
||||
|
||||
```shell
|
||||
curl -X OPTIONS -i http://localhost:3000/myfile.txt
|
||||
```
|
||||
|
||||
172
documentation/identity-provider.md
Normal file
172
documentation/identity-provider.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Identity Provider
|
||||
|
||||
Besides implementing the [Solid protocol](https://solidproject.org/TR/protocol),
|
||||
the community server can also be an Identity Provider (IDP), officially known as an OpenID Provider (OP),
|
||||
following the [Solid OIDC spec](https://solid.github.io/solid-oidc/) as much as possible.
|
||||
|
||||
It is recommended to use the latest version
|
||||
of the [Solid authentication client](https://github.com/inrupt/solid-client-authn-js)
|
||||
to interact with the server.
|
||||
|
||||
The links here assume the server is hosted at `http://localhost:3000/`.
|
||||
|
||||
## Registering an account
|
||||
To register an account, you can go to `http://localhost:3000/idp/register/` if this feature is enabled,
|
||||
which it is on all configurations we provide.
|
||||
Currently our registration page ties 3 features together on the same page:
|
||||
* Creating an account on the server.
|
||||
* Creating or linking a WebID to your account.
|
||||
* Creating a pod on the server.
|
||||
|
||||
### Account
|
||||
To create an account you need to provide an email address and password.
|
||||
The password will be salted and hashed before being stored.
|
||||
As of now, the account is only used to log in and identify yourself to the IDP
|
||||
when you want to do an authenticated request,
|
||||
but in future the plan is to also use this for account/pod management.
|
||||
|
||||
### WebID
|
||||
We require each account to have a corresponding WebID.
|
||||
You can either let the server create a WebID for you in a pod,
|
||||
which will also need to be created then,
|
||||
or you can link an already existing WebID you have on an external server.
|
||||
|
||||
In case you try to link your own WebID, you can choose if you want to be able
|
||||
to use this server as your IDP for this WebID.
|
||||
If not, you can still create a pod,
|
||||
but you will not be able to direct the authentication client to this server to identify yourself.
|
||||
|
||||
Additionally, if you try to register with an external WebID,
|
||||
the first attempt will return an error indicating you need to add an identification triple to your WebID.
|
||||
After doing that you can try to register again.
|
||||
This is how we verify you are the owner of that WebID.
|
||||
After registration the next page will inform you
|
||||
that you have to add an additional triple to your WebID if you want to use the server as your IDP.
|
||||
|
||||
All of the above is automated if you create the WebID on the server itself.
|
||||
|
||||
### Pod
|
||||
To create a pod you simply have to fill in the name you want your pod to have.
|
||||
This will then be used to generate the full URL of your pod.
|
||||
For example, if you choose the name `test`,
|
||||
your pod would be located at `http://localhost:3000/test/`
|
||||
and your generated WebID would be `http://localhost:3000/test/profile/card#me`.
|
||||
|
||||
The generated name also depends on the configuration you chose for your server.
|
||||
If you are using the subdomain feature,
|
||||
such as being done in the `config/memory-subdomains.json` configuration,
|
||||
the generated pod URL would be `http://test.localhost:3000/`.
|
||||
|
||||
## Logging in
|
||||
When using an authenticating client,
|
||||
you will be redirected to a login screen asking for your email and password.
|
||||
After that you will be redirected to a page showing some basic information about the client.
|
||||
There you need to consent that this client is allowed to identify using your WebID.
|
||||
As a result the server will send a token back to the client
|
||||
that contains all the information needed to use your WebID.
|
||||
|
||||
## Forgot password
|
||||
If you forgot your password, you can recover it by going to `http://localhost:3000/idp/forgotpassword/`.
|
||||
There you can enter your email address to get a recovery mail to reset your password.
|
||||
This feature only works if a mail server was configured,
|
||||
which by default is not the case.
|
||||
|
||||
## JSON API
|
||||
All of the above happens through HTML pages provided by the server.
|
||||
By default, the server uses the templates found in `/templates/identity/email-password/`
|
||||
but different templates can be used through configuration.
|
||||
|
||||
These templates all make use of a JSON API exposed by the server.
|
||||
For example, when doing a GET request to `http://localhost:3000/idp/register/`
|
||||
with a JSON accept header, the following JSON is returned:
|
||||
```json
|
||||
{
|
||||
"required": {
|
||||
"email": "string",
|
||||
"password": "string",
|
||||
"confirmPassword": "string",
|
||||
"createWebId": "boolean",
|
||||
"register": "boolean",
|
||||
"createPod": "boolean",
|
||||
"rootPod": "boolean"
|
||||
},
|
||||
"optional": {
|
||||
"webId": "string",
|
||||
"podName": "string",
|
||||
"template": "string"
|
||||
},
|
||||
"controls": {
|
||||
"register": "http://localhost:3000/idp/register/",
|
||||
"index": "http://localhost:3000/idp/",
|
||||
"prompt": "http://localhost:3000/idp/prompt/",
|
||||
"login": "http://localhost:3000/idp/login/",
|
||||
"forgotPassword": "http://localhost:3000/idp/forgotpassword/"
|
||||
},
|
||||
"apiVersion": "0.3"
|
||||
}
|
||||
```
|
||||
The `required` and `optional` fields indicate which input fields are expected by the API.
|
||||
These correspond to the fields of the HTML registration page.
|
||||
To register a user, you can do a POST request with a JSON body containing the correct fields:
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com",
|
||||
"password": "secret",
|
||||
"confirmPassword": "secret",
|
||||
"createWebId": true,
|
||||
"register": true,
|
||||
"createPod": true,
|
||||
"rootPod": false,
|
||||
"podName": "test"
|
||||
}
|
||||
```
|
||||
Two fields here that are not covered on the HTML page above are `rootPod` and `template`.
|
||||
`rootPod` tells the server to put the pod in the root of the server instead of a location based on the `podName`.
|
||||
By default the server will reject requests where this is `true`, except during setup.
|
||||
`template` is only used by servers running the `config/dynamic.json` configuration,
|
||||
which is a very custom setup where every pod can have a different Components.js configuration,
|
||||
so this value can usually be ignored.
|
||||
|
||||
## IDP configuration
|
||||
The above descriptions cover server behaviour with most default configurations,
|
||||
but just like any other feature, there are several features that can be changed
|
||||
through the imports in your configuration file.
|
||||
|
||||
All available options can be found in
|
||||
the [`config/identity/` folder](https://github.com/CommunitySolidServer/CommunitySolidServer/tree/main/config/identity).
|
||||
Below we go a bit deeper into the available options
|
||||
|
||||
### access
|
||||
The `access` option allows you to set authorization restrictions on the IDP API when enabled,
|
||||
similar to how authorization works on the LDP requests on the server.
|
||||
For example, if the server uses WebACL as authorization scheme,
|
||||
you can put a `.acl` resource in the `/idp/register/` container to restrict
|
||||
who is allowed to access the registration API.
|
||||
Note that for everything to work there needs to be a `.acl` resource in `/idp/` when using WebACL
|
||||
so resources can be accessed as usual when the server starts up.
|
||||
Make sure you change the permissions on `/idp/.acl` so not everyone can modify those.
|
||||
|
||||
All of the above is only relevant if you use the `restricted.json` setting for this import.
|
||||
When you use `public.json` the API will simply always be accessible by everyone.
|
||||
|
||||
### email
|
||||
In case you want users to be able to reset their password when they forget it,
|
||||
you will need to tell the server which email server to use to send reset mails.
|
||||
`example.json` contains an example of what this looks like,
|
||||
which you will need to copy over to your base configuration and then remove the `config/identity/email` import.
|
||||
|
||||
### handler
|
||||
There is only one option here. This import contains all the core components necessary to make the IDP work.
|
||||
In case you need to make some changes to core IDP settings, this is where you would have to look.
|
||||
|
||||
### pod
|
||||
The `pod` options determines how pods are created. `static.json` is the expected pod behaviour as described above.
|
||||
`dynamic.json` is an experimental feature that allows users
|
||||
to have a custom Components.js configuration for their own pod.
|
||||
When using such a setup, a JSON file will be written containing all the information of the user pods
|
||||
so they can be recreated when the server restarts.
|
||||
|
||||
### registration
|
||||
This setting allows you to enable/disable registration on the server.
|
||||
Disabling registration here does not disable registration during setup,
|
||||
meaning you can still use this server as an IDP with the account created there.
|
||||
0
documentation/idp.md
Normal file
0
documentation/idp.md
Normal file
33
documentation/making-changes.md
Normal file
33
documentation/making-changes.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Pull requests
|
||||
The community server is fully written in [Typescript](https://www.typescriptlang.org/docs/home.html).
|
||||
|
||||
All changes should be done through
|
||||
[pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork).
|
||||
|
||||
We recommend first discussing a possible solution in the relevant issue
|
||||
to reduce the amount of changes that will be requested.
|
||||
|
||||
In case any of your changes are breaking, make sure you target the next major branch (`versions/x.0.0`)
|
||||
instead of the main branch. Breaking changes include: changing interface/class signatures,
|
||||
potentially breaking external custom configurations,
|
||||
and breaking how internal data is stored.
|
||||
In case of doubt you probably want to target the next major branch.
|
||||
|
||||
We make use of [Conventional Commits](https://www.conventionalcommits.org).
|
||||
|
||||
Don't forget to update the [release notes](https://github.com/CommunitySolidServer/CommunitySolidServer/blob/main/RELEASE_NOTES.md)
|
||||
when adding new major features.
|
||||
Also update any relevant documentation in case this is needed.
|
||||
|
||||
When making changes to a pull request,
|
||||
we prefer to update the existing commits with a rebase instead of appending new commits,
|
||||
this way the PR can be rebased directly onto the target branch
|
||||
instead of needing to be squashed.
|
||||
|
||||
There are strict requirements from the linter and the test coverage before a PR is valid.
|
||||
These are configured to run automatically when trying to commit to git.
|
||||
Although there are no tests for it (yet), we strongly advice documenting with [TSdoc](https://github.com/microsoft/tsdoc).
|
||||
|
||||
If a list of entries is alphabetically sorted,
|
||||
such as [index.ts](https://github.com/CommunitySolidServer/CommunitySolidServer/blob/main/src/index.ts),
|
||||
make sure it stays that way.
|
||||
30
documentation/release.md
Normal file
30
documentation/release.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Releasing a new version
|
||||
|
||||
This is only relevant if you are a developer with push access responsible for doing a new release.
|
||||
|
||||
Steps to follow:
|
||||
* Merge `main` into `versions/x.0.0`.
|
||||
* Verify if there are issues when upgrading an existing installation to the new version.
|
||||
* Can the data still be accessed?
|
||||
* Does authentication still work?
|
||||
* Is there an issue upgrading the recipes at https://github.com/CommunitySolidServer/recipes
|
||||
* None of the above has to be blocking per se, but should be noted in the release notes if relevant.
|
||||
* Update all Components.js references to the new version.
|
||||
* All contexts in all configs to
|
||||
`https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^x.0.0/components/context.jsonld`.
|
||||
* Update all `lsd` entries in `package.json` to the new version.
|
||||
* Commit this with `chore: Update configs to vx.0.0`.
|
||||
* `npm version major -m "Release version %s of the npm package."`
|
||||
* This will update the `package.json`, generate a tag, and generate the new entries in `CHANGELOG.md`.
|
||||
* Manually edit the `CHANGELOG.md`.
|
||||
* First reverse the list of new entries so they go from old to new.
|
||||
* Put all entries in matching categories, look at the previous release for reference.
|
||||
* Most `chore` and `docs` entries can probably be removed.
|
||||
* Make sure there are 2 newlines between this and the previous section.
|
||||
* `git push --follow-tags`
|
||||
* Merge `versions/x.0.0` into `main`.
|
||||
* Do a GitHub release.
|
||||
* `npm publish`
|
||||
* Rename the `versions/x.0.0` branch to the next version.
|
||||
* Update `.github/workflows/schedule.yml` to point at the new branch.
|
||||
* Potentially upgrade the recipes at https://github.com/CommunitySolidServer/recipes
|
||||
75
documentation/resource-store.md
Normal file
75
documentation/resource-store.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Resource store
|
||||
Once an LDP request passes authorization, it will be passed to the `ResourceStore`.
|
||||
|
||||
The interface of a `ResourceStore` is mostly a 1-to-1 mapping of the HTTP methods:
|
||||
|
||||
* GET: `getRepresentation`
|
||||
* PUT: `setRepresentation`
|
||||
* POST: `addResource`
|
||||
* DELETE: `deleteResource`
|
||||
* PATCH: `modifyResource`
|
||||
|
||||
The corresponding `OperationHandler` of the relevant method
|
||||
is responsible for calling the correct `ResourceStore` function.
|
||||
|
||||
In practice, the community server has multiple resource stores chained together,
|
||||
each handling a specific part of the request and then calling the next store in the chain.
|
||||
The default configurations come with the following stores:
|
||||
|
||||
1. `MonitoringStore`
|
||||
2. `IndexRepresentationStore`
|
||||
3. `LockingResourceStore`
|
||||
4. `PatchingStore`
|
||||
5. `RepresentationConvertingStore`
|
||||
6. `DataAccessorBasedStore`
|
||||
|
||||
This chain can be seen in the configuration part in `config/storage/middleware/default.json`
|
||||
and all the entries in `config/storage/backend`.
|
||||
|
||||
## MonitoringStore
|
||||
This store emits the events that are necessary to emit notifications when resources change.
|
||||
|
||||
## IndexRepresentationStore
|
||||
When doing a GET request on a container `/container/`,
|
||||
this container returns the contents of `/container/index.html` instead if HTML is the preferred response type.
|
||||
All these values are the defaults and can be configured for other resources and media types.
|
||||
|
||||
## LockingResourceStore
|
||||
To prevent data corruption, the server locks resources when being targeted by a request.
|
||||
Locks are only released when an operation is completely finished,
|
||||
in the case of read operations this means the entire data stream is read,
|
||||
and in the case of write operations this happens when all relevant data is written.
|
||||
The default lock that is used is a readers-writer lock.
|
||||
This allows simultaneous read requests on the same resource,
|
||||
but only while no write request is in progress.
|
||||
|
||||
## PatchingStore
|
||||
PATCH operations in Solid apply certain transformations on the target resource,
|
||||
which makes them more complicated than only reading or writing data since it involves both.
|
||||
The `PatchingStore` provides a generic solution for backends that do not implement the `modifyResource` function
|
||||
so new backends can be added more easily.
|
||||
In case the next store in the chain does not support PATCH,
|
||||
the `PatchingStore` will GET the data from the next store,
|
||||
apply the transformation on that data,
|
||||
and then PUT it back to the store.
|
||||
|
||||
## RepresentationConvertingStore
|
||||
This store handles everything related to content negotiation.
|
||||
In case the resulting data of a GET request does not match the preferences of a request,
|
||||
it will be converted here.
|
||||
Similarly, if incoming data does not match the type expected by the store,
|
||||
the SPARQL backend only accepts triples for example,
|
||||
that is also handled here
|
||||
|
||||
## DataAccessorBasedStore
|
||||
Large parts of the requirements of the Solid protocol specification are resolved by the `DataAccessorBasedStore`:
|
||||
POST only working on containers,
|
||||
DELETE not working on non-empty containers,
|
||||
generating `ldp:contains` triples for containers, etc.
|
||||
Most of this behaviour is independent of how the data is stored which is why it can be generalized here.
|
||||
The store's name comes from the fact that it makes use of `DataAccessor`s to handle the read/write of resources.
|
||||
A `DataAccessor` is a simple interface that only focuses on handling the data.
|
||||
It does not concern itself with any of the necessary Solid checks as it assumes those have already been made.
|
||||
This means that if a storage method needs to be supported,
|
||||
only a new `DataAccessor` needs to be made,
|
||||
after which it can be plugged into the rest of the server.
|
||||
20
documentation/seeding-pods.md
Normal file
20
documentation/seeding-pods.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# How to seed Accounts and Pods
|
||||
If you need to seed accounts and pods, set the `--seededPodConfigJson` option to a file such as `./seeded-pod-config.json` to set your desired accounts and pods. The contents of `./seeded-pod-config.json` (or whatever file name you choose) should be a JSON array whose entries are objects which include
|
||||
`podName`, `email`, and `password`. For example:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"podName": "example",
|
||||
"email": "hello@example.com",
|
||||
"password": "abc123"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
You may optionally specify other parameters accepted by the `register` method of [RegistrationManager](https://github.com/solid/community-server/blob/3b353affb1f0919fdcb66172364234eb59c2e3f6/src/identity/interaction/email-password/util/RegistrationManager.ts#L173). For example:
|
||||
|
||||
To use a pre-existing wedId:
|
||||
```json
|
||||
createWebId: false,
|
||||
webId: "https://pod.inrupt.com/example/profile/card#me"
|
||||
```
|
||||
Reference in New Issue
Block a user