Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ make install # prompts for base URL, keystore path, keystore password and opt

Each `install.sh` sources `lib/ldh-app.sh` and is the app's own sequence of steps:

- `ldh admin make-public` — an authorization that makes the app readable by anyone
- `ldh admin make-public` — grants that make the app readable by anyone, the root and everything below it
- `ldh_app_import_ns admin/model` — `ldh admin import ontology --file admin/model/ns.ttl`: installs `ns.ttl` as the namespace ontology, replacing the document and clearing the ontology cache
- `ldh push --dir . "$LDH_BASE"` — PUTs every RDF file to the document its path spells and uploads every other file into its folder's document
- `ldh_app_import_csv . imports.csv` — one `ldh import csv --wait` per manifest row (`query_filename,csv_filename,target,title`)
- app-specific steps: `ldh admin create authorization`, `ldh packages add`, `ldh import rdf`

**Re-runs converge but are not clean.** `ldh push` PUTs and the ontology is reset before re-import, so documents and the model end up the same. `make-public`, `create authorization` and every import are POSTs, so each run adds another authorization and another import record. Nothing probes for an earlier install.
**Re-runs converge but are not clean.** `ldh push` PUTs and the ontology is reset before re-import, so documents and the model end up the same. `make-public` writes the same grants again. `create authorization` and every import are POSTs, so each run adds another authorization and another import record. Nothing probes for an earlier install.

### App layout

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ cd ../LinkedDataHub/cli && mvn package && export PATH="$PWD/bin:$PATH"

`make install` prompts for the base URL, the owner's keystore, its password and an optional proxy URL, exports them as the `LDH_*` variables the CLI reads, and runs the app's `install.sh` — every installer sources the shared [`lib/ldh-app.sh`](lib/ldh-app.sh). To install unattended, export the variables and run `./install.sh`.

__Re-running an install converges but is not clean: `ldh push` replaces each document and the namespace ontology is reset before re-import, but `make-public`, `create authorization` and every import are POSTs, so each run adds another authorization and another import record.__
__Re-running an install converges but is not clean: `ldh push` replaces each document and the namespace ontology is reset before re-import, and `make-public` writes the same grants again, but `create authorization` and every import are POSTs, so each run adds another authorization and another import record.__

## Apps

Expand Down
4 changes: 2 additions & 2 deletions demo/copenhagen/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#
# Reads LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and optionally LDH_PROXY; `make install` prompts
# for them. Re-running converges on the same documents — PUT replaces each one and the ontology is
# reset before re-import — but make-public and every CSV import are POSTs, so each run adds another
# authorization and another import record.
# reset before re-import, and make-public writes the same grants again — but every CSV import is a
# POST, so each run adds another import record.
set -euo pipefail

app_dir="$(cd "$(dirname "$0")" && pwd)"
Expand Down
4 changes: 2 additions & 2 deletions demo/northwind-traders/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#
# Reads LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and optionally LDH_PROXY; `make install` prompts
# for them. Re-running converges on the same documents — PUT replaces each one and the ontology is
# reset before re-import — but make-public and every CSV import are POSTs, so each run adds another
# authorization and another import record.
# reset before re-import, and make-public writes the same grants again — but every CSV import is a
# POST, so each run adds another import record.
set -euo pipefail

app_dir="$(cd "$(dirname "$0")" && pwd)"
Expand Down
17 changes: 5 additions & 12 deletions demo/unesco-thesaurus/install.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#!/usr/bin/env bash
# Installs the app onto a LinkedDataHub instance with the ldh CLI: makes it public, grants
# authenticated agents read access to its items, pushes the document tree with its files, imports the
# taxonomy editor package and imports the UNESCO Thesaurus SKOS data. The ontology, constructors,
# Installs the app onto a LinkedDataHub instance with the ldh CLI: makes it public, pushes the
# document tree with its files, imports the taxonomy editor package and imports the UNESCO Thesaurus
# SKOS data. The ontology, constructors,
# constraints and stylesheet all come from the package.
#
# Reads LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and optionally LDH_PROXY; `make install` prompts
# for them. Re-running converges on the same documents — PUT replaces each one — but make-public,
# the authorization and the RDF import are POSTs, so each run adds another of each.
# for them. Re-running converges on the same documents — PUT replaces each one and make-public
# writes the same grants again — but the RDF import is a POST, so each run adds another.
set -euo pipefail

app_dir="$(cd "$(dirname "$0")" && pwd)"
Expand All @@ -16,13 +16,6 @@ ldh_app_require_env
ldh_app_step "Creating authorization to make the app public"
ldh admin make-public

ldh_app_step "Creating authorization for authenticated agents to read items"
ldh admin create authorization \
--label "Read access to graph items" \
--agent-class "http://www.w3.org/ns/auth/acl#AuthenticatedAgent" \
--to-all-in "https://w3id.org/atomgraph/linkeddatahub/document-hierarchy#Item" \
--read

ldh_app_step "Pushing documents and files"
ldh push --dir "$app_dir" "$LDH_BASE"

Expand Down
Binary file modified docs/get-started/setup-1.mp4
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/get-started/setup.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<#content> a ldh:XHTML ;
rdf:value """<div xmlns="http://www.w3.org/1999/xhtml">
<p class="ldh-body-lg">Set up LinkedDataHub on your local machine with Docker</p>
<video aria-label="Setting up LinkedDataHub with Docker" controls="controls" preload="metadata" src="/uploads/7caf1576295c948e3639e468c5398ed3c787f6c4"></video>
<video aria-label="Setting up LinkedDataHub with Docker" controls="controls" preload="metadata" src="/uploads/b90f8d886becfea648bd1118b301328ee47d923e"></video>
<div>
<h2 id="start-linkeddatahub">Start LinkedDataHub</h2>
<p>This section assumes you will be running on your local machine, i.e. <samp>localhost</samp>. If you intend to run it on a different host, <a href="../../reference/configuration/#system-base-uri">change the system base URI</a>.</p>
Expand Down
4 changes: 2 additions & 2 deletions docs/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# the sources reference.
#
# Reads LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and optionally LDH_PROXY; `make install` prompts
# for them. Re-running converges: PUT replaces each document. make-public is a POST and adds another
# authorization per run.
# for them. Re-running converges: PUT replaces each document and make-public writes the same grants
# again.
set -euo pipefail

app_dir="$(cd "$(dirname "$0")" && pwd)"
Expand Down
69 changes: 48 additions & 21 deletions docs/reference/administration/acl.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<p>LinkedDataHub access control is based on the W3C <a href="https://www.w3.org/wiki/WebAccessControl" target="_blank">ACL ontology</a>.</p>
<div>
<h2 id="access-modes">Access modes</h2>
<p>There are four access modes (classes of operation) that map to <a href="../../http-api/documents/#methods">HTTP methods</a>:</p>
<p>There are three access modes (classes of operation) that map to <a href="../../http-api/documents/#methods">HTTP methods</a>:</p>
<table>
<caption class="ac-vh">Access modes and the HTTP methods they permit</caption>
<thead>
Expand All @@ -40,13 +40,10 @@
<td>Add information without removing or replacing any.</td>
<td><code>POST</code></td>
</tr>
<tr>
<th scope="row">Control</th>
<td>Change the authorizations that apply to the resource.</td>
<td>— (no corresponding HTTP method)</td>
</tr>
</tbody>
</table>
<p><code>acl:Control</code> grants nothing: authorizations are documents in the <a href="../../dataspace/#admin">administration dataspace</a>,
so changing them takes <code>acl:Write</code> there.</p>
</div>
<div>
<h2 id="agents">Agents</h2>
Expand All @@ -68,8 +65,8 @@
<span class="ac-alert-ic"><span aria-hidden="true" class="msi outline">info</span></span>
<div class="ac-alert-body">
<p class="ac-alert-text">By default, only members of the owners group have access to the administration dataspace: its built-in
<samp>Full control</samp> authorization grants the <a href="#access-modes">Control</a> mode (along with read and write)
to that group. See <a href="#authorizations">Authorizations</a>.</p>
<samp>Full control</samp> authorization grants them read, append and write access to the administration root and everything
below it. See <a href="#authorizations">Authorizations</a>.</p>
<p class="ac-alert-text">Note that an agent being a member of one of the above groups does not automatically provide it with an
authorization. A valid authorization for the whole group has to be present.</p>
</div>
Expand All @@ -78,11 +75,13 @@
<div>
<h2 id="authorizations">Authorizations</h2>
<p>An authorization explicitly grants access for an <a href="#agents">agent</a> or a <a href="#groups">group</a>
of agents to access a specific end-user dataspace document or a <a href="../ontologies/#classes">class</a>
of its documents.</p>
<p>An <a href="#agents">agent</a> has to be authorized using the <a href="#access-modes">Control</a> mode to be
able to log in to the <a href="../../dataspace/#admin">administration dataspace</a>. By default such an
authorization is granted to the <a href="#groups">owners group</a>.</p>
of agents to a specific document (<code>acl:accessTo</code>), to every document below a container (<code>acl:default</code>),
or to the resources of a <a href="../ontologies/#classes">class</a> (<code>acl:accessToClass</code>). Grants only add access:
no authorization overrides another.</p>
<p>An authorization counts only in its own document, the one whose <code>foaf:primaryTopic</code> it is, and a group's members count
only in the group's own document.</p>
<p>To use the <a href="../../dataspace/#admin">administration dataspace</a>, an <a href="#agents">agent</a> needs access to its
documents like to any others. By default the <a href="#groups">owners group</a> has it.</p>
<p>The default authorizations for groups and their respective access modes:</p>
<table>
<caption class="ac-vh">Default access for each group</caption>
Expand All @@ -91,15 +90,15 @@
<th scope="col">Group</th>
<th scope="col">Read access</th>
<th scope="col">Write/append access</th>
<th scope="col">Full control</th>
<th scope="col">Administration dataspace</th>
</tr>
</thead>
<tbody>
<tr>
<th rowspan="2" scope="row">Owners</th>
<td rowspan="2"><a href="#access-modes">Read</a></td>
<td><a href="#access-modes">Write</a></td>
<td rowspan="2"><a href="#access-modes">Control</a></td>
<td rowspan="2"><a href="#access-modes">Read</a>, <a href="#access-modes">Append</a>, <a href="#access-modes">Write</a></td>
</tr>
<tr>
<td><a href="#access-modes">Append</a></td>
Expand Down Expand Up @@ -145,19 +144,47 @@
--read --append --write</pre>
</div>
</div>
<p>An authorization can also target a whole class of documents with <code>acl:accessToClass</code>. Granting the <samp>readers</samp> group read access to every container:</p>
<pre>@prefix acl: &lt;http://www.w3.org/ns/auth/acl#&gt; .
<p>An authorization can also reach every document below a container with <code>acl:default</code>. Granting the <samp>readers</samp> group read access to
the orders container and every order in it:</p>
<div class="ac-tabs or-horizontal">
<div class="ac-tablist sz-md va-line" role="tablist">
<button aria-controls="default-rdf-pane" aria-selected="true" class="ac-tab is-on" id="default-rdf-tab" role="tab" type="button"><span class="ac-tab-lbl">RDF</span></button>
<button aria-controls="default-command-line-interface-pane" aria-selected="false" class="ac-tab" id="default-command-line-interface-tab" role="tab" type="button"><span class="ac-tab-lbl">Command line interface</span></button>
</div>
<div aria-labelledby="default-rdf-tab" class="ac-tabpanel" id="default-rdf-pane" role="tabpanel">
<pre>@prefix acl: &lt;http://www.w3.org/ns/auth/acl#&gt; .
@prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .
@prefix dh: &lt;https://w3id.org/atomgraph/linkeddatahub/document-hierarchy#&gt; .

&lt;#this&gt; a acl:Authorization ;
rdfs:label "Container read access" ;
rdfs:label "Order read access" ;
acl:agentGroup &lt;https://admin.localhost:4443/acl/groups/readers/#this&gt; ;
acl:mode acl:Read ;
acl:accessToClass dh:Container .</pre>
acl:accessTo &lt;https://localhost:4443/orders/&gt; ;
acl:default &lt;https://localhost:4443/orders/&gt; .</pre>
</div>
<div aria-labelledby="default-command-line-interface-tab" class="ac-tabpanel" hidden="hidden" id="default-command-line-interface-pane" role="tabpanel">
<pre>ldh admin create authorization \\
--label "Order read access" \\
--agent-group "https://admin.localhost:4443/acl/groups/readers/#this" \\
--to "https://localhost:4443/orders/" \\
--default "https://localhost:4443/orders/" \\
--read</pre>
</div>
</div>
<p>A document is below a container when its own <code>sioc:has_parent</code> or <code>sioc:has_container</code> leads there. <code>acl:default</code> does
not cover the container itself, hence the <code>acl:accessTo</code> alongside it.</p>
<p><code>acl:accessToClass</code> grants access to the resources of a class, such as <code>nfo:FileDataObject</code> for file uploads. It refuses the
document classes (<code>def:Root</code>, <code>dh:Container</code>, <code>dh:Item</code>) and the classes above them: documents are granted through the
hierarchy.</p>
<p>A public-access authorization allows access for non-authenticated agents — the <a href="../../../tutorial/publish/">Publish</a> stage of the
tutorial shows the one <samp>ldh admin make-public</samp> creates, granting <code>acl:Read</code> to the <code>foaf:Agent</code> class
on every document class.</p>
on the root and every document below it.</p>
<div>
<h3 id="owners">Document owners</h3>
<p>The agent that creates a document is its owner (<code>acl:owner</code>), and stays so for the life of the document. The built-in
<samp>Owner access</samp> authorization grants read, append and write access to the <code>lacl:OwnerAgent</code> class, which matches
the owners of the requested document, on the end-user root and every document below it.</p>
</div>
<div class="ac-alert va-informative" role="status">
<span class="ac-alert-ic"><span aria-hidden="true" class="msi outline">info</span></span>
<div class="ac-alert-body">
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/command-line-interface.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ ldh create container --parent "$LDH_BASE" --title "Categories" --slug categories
<pre>item=$(ldh create item --container "$LDH_BASE" --title "Example" --slug example)</pre>
<p><samp>ldh push</samp> writes many documents in one run and prints one line per written document URL or upload URI, in write order; progress and skipped entries go to standard error, and the first failed request stops the run.</p>
<p>Exit codes are <samp>0</samp> on success, <samp>1</samp> on an HTTP error status or runtime failure (the message goes to standard error; add <code>--verbose</code> for a stack trace), and <samp>2</samp> on a usage error.</p>
<p>Redirects are not followed and count as failures, with their <code>Location</code> on standard error: a <code>PUT</code> to a URI without a trailing slash is answered <samp>308 Permanent Redirect</samp> and writes nothing. A write answered <samp>303 See Other</samp> succeeds; <samp>ldh get --head</samp> reports any status, and <samp>ldh get --timegate</samp> takes its <samp>302 Found</samp>.</p>
</div>
<div>
<h2 id="usage">Usage</h2>
Expand Down Expand Up @@ -398,6 +399,7 @@ ldh add file \\
"$doc"</pre>
<p><samp>add file</samp> prints the content-addressed URI of the upload (<samp>{base}uploads/{sha1}</samp>) rather than the document URI — the
<a href="../../tutorial/data/">Data</a> stage of the tutorial feeds exactly that URI into a CSV import.</p>
<p>It appends the file's description to the document (a multipart <code>POST</code>); with <code>--replace</code> it replaces the document's description with it instead (a multipart <code>PUT</code>).</p>
<p>See also the <a href="../../user-guide/import-data/">data import user guides</a>.</p>
<h3 id="request-bodies">Request bodies</h3>
<p><samp>ldh put</samp> and <samp>ldh post</samp> read RDF from an optional <samp>FILE</samp> argument, recognizing the syntax from its extension (<samp>ldh put "$LDH_BASE" root.ttl</samp>), or from standard input, where <code>-t</code>/<code>--content-type</code> is required since a stream carries no name. Either way relative URIs resolve against the target URI. <samp>ldh patch</samp> reads a SPARQL 1.1 update from standard input, validates it and sends it verbatim.</p>
Expand Down
4 changes: 3 additions & 1 deletion docs/reference/configuration.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com</pre>
<dd>Set to <samp>false</samp> if you are not using the self-signed <em>server</em> certificate (e.g. using a LetsEncrypt certificate instead). Not to be confused with the WebID client certificate.
Defaults to <samp>true</samp>.</dd>
<dt><samp>MAX_CONTENT_LENGTH</samp></dt>
<dd>Maximum allowed request body size (<samp>nginx</samp> has a separate setting for this). Defaults to <samp>2097152</samp>.</dd>
<dd>Maximum allowed request body size, in bytes; a larger body is refused with <samp>413 Payload Too Large</samp>. Defaults to <samp>2097152</samp>.</dd>
<dt><samp>MAX_UPLOAD_LENGTH</samp></dt>
<dd>Maximum size, in bytes, of a file upload (a <code>multipart/form-data</code> request body); a larger one is refused with <samp>413 Payload Too Large</samp>. Defaults to <samp>MAX_CONTENT_LENGTH</samp>. <samp>nginx</samp> limits request bodies to this value when it is set, and to <samp>MAX_CONTENT_LENGTH</samp> otherwise.</dd>
<dt><samp>MAX_QUERY_RESPONSE_LENGTH</samp></dt>
<dd>The longest query result, in bytes, that the <a href="../http-api/sparql/"><samp>/sparql</samp> endpoint</a> answers a query with; a longer one is refused with <samp>502 Bad Gateway</samp>. Counted on the triplestore's response to the platform, not on what the caller receives. Defaults to <samp>5242880</samp> (5 MiB).</dd>
<dt><samp>HTTP_MAX_THREADS</samp></dt>
Expand Down
Loading
Loading