flowchart TD AuthorizingHttpHandler("<br>AuthorizingHttpHandler") AuthorizingHttpHandler --> AuthorizingHttpHandlerArgs subgraph AuthorizingHttpHandlerArgs[" "] CredentialsExtractor("<strong>CredentialsExtractor</strong><br><i>CredentialsExtractor</i>") ModesExtractor("<strong>ModesExtractor</strong><br><i>ModesExtractor</i>") PermissionReader("<strong>PermissionReader</strong><br><i>PermissionReader</i>") Authorizer("<strong>Authorizer</strong><br>PermissionBasedAuthorizer") OperationHttpHandler("<br><i>OperationHttpHandler</i>") end
Authorization is usually handled by the
which receives a parsed HTTP request in the form of an
It goes through the following steps:
CredentialsExtractoridentifies the credentials of the agent making the call.
ModesExtractorfinds which access modes are needed for which resources.
PermissionReaderdetermines the permissions the agent has on the targeted resources.
- The above results are compared in an
- If the request is allowed, call the
OperationHttpHandler, otherwise throw an error.
There are multiple
CredentialsExtractors that each determine identity in a different way.
Potentially multiple extractors can apply,
making a requesting agent have multiple credentials.
The diagram below shows the default configuration if authentication is enabled.
flowchart TD CredentialsExtractor("<strong>CredentialsExtractor</strong><br>UnionCredentialsExtractor") CredentialsExtractor --> CredentialsExtractorArgs subgraph CredentialsExtractorArgs[" "] WaterfallHandler("<br>WaterfallHandler") PublicCredentialsExtractor("<br>PublicCredentialsExtractor") end WaterfallHandler --> WaterfallHandlerArgs subgraph WaterfallHandlerArgs[" "] direction LR DPoPWebIdExtractor("<br>DPoPWebIdExtractor") --> BearerWebIdExtractor("<br>BearerWebIdExtractor") end
Both of the WebID extractors make use of
to parse incoming tokens based on the Solid-OIDC specification.
Besides those there are always the public credentials, which everyone has.
All these credentials then get combined into a single union object.
If successful, a
CredentialsExtractor will return a key/value map
linking the type of credentials to their specific values.
There are also debug configuration options available that can be used to simulate credentials.
These can be enabled as different options through the
Access modes are a predefined list of
ModesExtractor determine which modes will be necessary and for which resources,
based on the request contents.
flowchart TD ModesExtractor("<strong>ModesExtractor</strong><br>IntermediateCreateExtractor") ModesExtractor --> HttpModesExtractor("<strong>HttpModesExtractor</strong><br>WaterfallHandler") HttpModesExtractor --> HttpModesExtractorArgs subgraph HttpModesExtractorArgs[" "] direction LR PatchModesExtractor("<strong>PatchModesExtractor</strong><br><i>ModesExtractor</i>") --> MethodModesExtractor("<br>MethodModesExtractor") end
IntermediateCreateExtractor is responsible if requests try to create intermediate containers with a single request.
E.g., a PUT request to
/foo/bar/baz should create both the
/foo/bar/ containers in case they do not
This extractor makes sure that
create permissions are also checked on those containers.
Modes can usually be determined based on just the HTTP methods,
which is what the
A GET request will always need the
read mode for example.
The only exception are PATCH requests, where the necessary modes depend on the body and the PATCH type.
flowchart TD PatchModesExtractor("<strong>PatchModesExtractor</strong><br>WaterfallHandler") --> PatchModesExtractorArgs subgraph PatchModesExtractorArgs[" "] N3PatchModesExtractor("<br>N3PatchModesExtractor") SparqlUpdateModesExtractor("<br>SparqlUpdateModesExtractor") end
The server supports both N3 Patch and SPARQL Update PATCH requests. In both cases it will parse the bodies to determine what the impact would be of the request and what modes it requires.
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 returns all the information it can find based on the resources and modes it receives.
In the default configuration the following readers are combined when WebACL is enabled as authorization method.
In case authorization is disabled by changing the authorization import to
this diagram is just a class that always returns all permissions.
flowchart TD PermissionReader("<strong>PermissionReader</strong><br>AuxiliaryReader") PermissionReader --> UnionPermissionReader("<br>UnionPermissionReader") UnionPermissionReader --> UnionPermissionReaderArgs subgraph UnionPermissionReaderArgs[" "] PathBasedReader("<strong>PathBasedReader</strong><br>PathBasedReader") OwnerPermissionReader("<strong>OwnerPermissionReader</strong><br>OwnerPermissionReader") WrappedWebAclReader("<strong>WrappedWebAclReader</strong><br>ParentContainerReader") end WrappedWebAclReader --> WebAclAuxiliaryReader("<strong>WebAclAuxiliaryReader</strong><br>WebAclAuxiliaryReader") WebAclAuxiliaryReader --> WebAclReader("<strong>WebAclReader</strong><br>WebAclReader")
The first thing that happens is that if the target is an auxiliary resource that uses the authorization of its subject resource,
AuxiliaryReader inserts that identifier instead.
An example of this is if the requests targets the metadata of a resource.
UnionPermissionReader then combines the results of its readers into a single permission object.
If one reader rejects a specific mode and another allows it, the rejection takes priority.
PathBasedReader rejects all permissions for certain paths.
This is used to prevent access to the internal data of the server.
OwnerPermissionReader makes sure owners always have control access
to the pods they created on the server.
Users will always be able to modify the ACL resources in their pod,
even if they accidentally removed their own access.
The final readers are specifically relevant for the WebACL algorithm.
ParentContainerReader checks the permissions on a parent resource if required:
creating a resource requires
append permissions on the parent container,
while deleting a resource requires
write permissions there.
In case the target is an ACL resource,
control permissions need to be checked,
no matter what mode was generated by the
WebAclAuxiliaryReader makes sure this conversion happens.
the efffective ACL resource algorithm
and returns the permissions it finds in that resource.
In case no ACL resource is found this indicates a configuration error and no permissions will be granted.
All the results of the previous steps then get combined in the
PermissionBasedAuthorizer 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.