solid-client-js icon indicating copy to clipboard operation
solid-client-js copied to clipboard

Support appending to file without read/write access

Open Otto-AA opened this issue 3 years ago • 5 comments

Search terms you've used

I've searched for "patch" and "append"

Feature suggestion

The Solid rest-api describes that we can use a PATCH request to append data with SPARQL. I would like to see an implementation of this, which allows appending a Thing to a specific file with only append access.

Expected functionality/enhancement

Something along await appendToFile(url, thing). I'm not very familiar to the solid-client API, so there may be a better solution.

Actual functionality/enhancement

Currently it seems possible to update a file only if one has read and write access.

Use Cases

I would use it to let people append themself to secret groups.

Indirectly this allows granting access to a file shareMe.txt via a link:

  1. create file myGroup where everbody can append, but not read (so the content is secret)
  2. give a group with a cryptographically random name (asdfasdf) access to shareMe.txt
  3. create a Link which contains the random name (https://example-sharer.com/#key=asdfasdf)
  4. anybody with this link can append themself to group asdfasdf and thus has access to shareMe.txt

Additional information

Otto-AA avatar Mar 16 '22 11:03 Otto-AA

solid-client already sends PATCH requests when you build on top of a previously fetched SolidDataset. So if you first fetch the SolidDataset that contains the group, then add people to the group's Thing, then save that SolidDataset again, that should do what you want.

Vinnl avatar Mar 17 '22 13:03 Vinnl

Hi Vinnl, thanks for the fast response.

From my testing, saveSolidDatasetAt requires WRITE permissions to the file (and APPEND does not suffice, even if only adding to the file).

I've tested it with the script below which resulted in this output:

success: https://sheep.solidcommunity.net/private/editors.ttl
error: https://sheep.solidcommunity.net/private/posters.ttl - 401
error: https://sheep.solidcommunity.net/private/submitters.ttl - 401

The files are initially empty and have public access to WRITE (editors), READ (posters) and APPEND (posters, subbmitters).

This is the testing script:

const { saveSolidDatasetAt, createSolidDataset, createThing, buildThing, setThing } = require("@inrupt/solid-client")
const { RDF, SCHEMA_INRUPT } = require('@inrupt/vocab-common-rdf')

const main = async () => {
    let courseSolidDataset = createSolidDataset();
    const newBookThing1 = buildThing(createThing({ name: "book2" }))
        .addStringNoLocale(SCHEMA_INRUPT.name, "ABC123 of Example Literature")
        .addUrl(RDF.type, "https://schema.org/Book")
        .build();
    courseSolidDataset = setThing(courseSolidDataset, newBookThing1);

    await testSaving('https://sheep.solidcommunity.net/private/editors.ttl', courseSolidDataset)
    await testSaving('https://sheep.solidcommunity.net/private/posters.ttl', courseSolidDataset)
    await testSaving('https://sheep.solidcommunity.net/private/submitters.ttl', courseSolidDataset)
}

const testSaving = async(url, dataset) => {
    try {
        const savedSolidDataset = await saveSolidDatasetAt(url, dataset);
        console.log(`success: ${url}`)
    } catch (err) {
        console.log(`error: ${url} - ${err.response?.status ?? err.message}`)
    }
}

main()

Otto-AA avatar Mar 19 '22 10:03 Otto-AA

@Otto-AA Yes, that's why I said "when you build on top of a previously fetched SolidDataset". You are creating a new SolidDataset every time (createSolidDataset()), so solid-client will attempt to overwrite whatever is already at that location (hence requiring Write permissions). Off the top of my head (i.e. I didn't run this to test), if I change your code to the following, it should only require Append access:

const { saveSolidDatasetAt, createSolidDataset, createThing, buildThing, setThing } = require("@inrupt/solid-client")
const { RDF, SCHEMA_INRUPT } = require('@inrupt/vocab-common-rdf')

const main = async () => {
    let editorsSolidDataset = await getSolidDataset("https://sheep.solidcommunity.net/private/editors.ttl");
    const newBookThing1 = buildThing(createThing({ name: "book2" }))
        .addStringNoLocale(SCHEMA_INRUPT.name, "ABC123 of Example Literature")
        .addUrl(RDF.type, "https://schema.org/Book")
        .build();
    editorsSolidDataset = setThing(editorsSolidDataset, newBookThing1);

    await testSaving('https://sheep.solidcommunity.net/private/editors.ttl', editorsSolidDataset);
}

const testSaving = async(url, dataset) => {
    try {
        const savedSolidDataset = await saveSolidDatasetAt(url, dataset);
        console.log(`success: ${url}`)
    } catch (err) {
        console.log(`error: ${url} - ${err.response?.status ?? err.message}`)
    }
}

main()

Vinnl avatar Mar 19 '22 11:03 Vinnl

Sorry if I was not clear enough, or I'm still not getting your point.

As far as I understand your example requires READ access (getSolidDataset fails without READ access). The spec and at least NSS support appending with only APPEND access (even without READ and WRITE). This can be done with a INSERT SPARQL statement I think (but I don't know if this is defined in the spec, or only implemented this way by NSS).

This is what I try to achieve: appending without being able to read or modify existing contents.

Otto-AA avatar Mar 19 '22 12:03 Otto-AA

Ah yes, if you want to append without read access, that would indeed need an addition to the API, e.g. something like saveSolidDatasetAt("https://example.com", { forceAppend: true }). (Also note that I don't work on solid-client/at Inrupt any more, so someone else will have to do that :slightly_smiling_face: )

Vinnl avatar Mar 19 '22 13:03 Vinnl