SyncServerII / ServerSolidAccount

Server-side Solid Pod Account for SyncServerII
MIT License
0 stars 0 forks source link

Make sure I've got the hostURL right #4

Open crspybits opened 3 years ago

crspybits commented 3 years ago

This is in reference to the hostURL field in SolidCreds but has implications for my entire auth chain of code for Solid.

The conversation here https://forum.solidproject.org/t/basic-question-about-url-to-use-when-making-a-request/4605/22

revealed that I can't just use the "host" of a URL for a WebID to get the base url to use when making HTTP requests of a Solid Pod. While this works for https://crspybits.inrupt.net/profile/card#me (i.e., the host component of the URL is https://crspybits.inrupt.net/), it does not work for https://pod.inrupt.com/crspybits/profile/card#me where the host is https://pod.inrupt.com.

There are several actions needed here:

1) In my iOSSolid package, it looks like I need to overtly ask the user for this base URL, in addition to asking for the issuer URL. Not sure what terminology should be used.

2) In the current package, I'm not sure about the Host header passed in HTTP requests. I'm not sure if this should be this base URL or if it should truly be the host component.

3) I need to run a test of the current package making use of a webid like https://pod.inrupt.com/crspybits/profile/card#me to make sure it works in those cases.

crspybits commented 3 years ago

"preferred storage location." might be the term (see https://gitter.im/solid/app-development)

crspybits commented 3 years ago

jeff-zucker

"I think that "preferred storage location" is good; "where in your pod do you want app data for this app stored" if you want something more explanatory. You could also try looking in the profile for a triple like <#me> pim:storage , and if not found, ask the user. That triple isn't guaranteed to be there but it often is. Also many apps use the publicTypeIndex.ttl to store app location data. That also isn't guaranteed to be there but often is." (see https://gitter.im/solid/app-development)

"You get the WebID returned from the login so you don't need to ask the user for it ... I think you should ask in the forum about the general approach - I've given you the strict spec view but others may have more flexible solutions. Look at Noel de Martin's software - he does a bunch of this kind of onbording."

crspybits commented 3 years ago

MacBook-Pro-4:~ chris$ curl https://crspybits.solidcommunity.net/profile/card#me

Gives:

@prefix : <#>.
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@prefix ldp: <http://www.w3.org/ns/ldp#>.
@prefix schema: <http://schema.org/>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
@prefix space: <http://www.w3.org/ns/pim/space#>.
@prefix pro: <./>.
@prefix inbox: </inbox/>.
@prefix crs: </>.

pro:card a foaf:PersonalProfileDocument; foaf:maker :me; foaf:primaryTopic :me.

:me
    a schema:Person, foaf:Person;
    acl:trustedApp
            [
                acl:mode acl:Append, acl:Read, acl:Write;
                acl:origin <biz.spasticmuffin.neebla.demo://>
            ],
            [
                acl:mode acl:Append, acl:Read, acl:Write;
                acl:origin <biz.spasticmuffin.neebla.solidsignin://>
            ],
            [
                acl:mode acl:Append, acl:Read, acl:Write;
                acl:origin <biz.spasticmuffin.solid.demo://>
            ],
            [
                acl:mode acl:Append, acl:Read, acl:Write;
                acl:origin <com.wm.pod-browser://>
            ],
            [
                acl:mode acl:Append, acl:Read, acl:Write;
                acl:origin <https://ramen.noeldemartin.com>
            ];
    ldp:inbox inbox:;
    space:preferencesFile </settings/prefs.ttl>;
    space:storage crs:;
    solid:account crs:;
    solid:privateTypeIndex </settings/privateTypeIndex.ttl>;
    solid:publicTypeIndex </settings/publicTypeIndex.ttl>;
    foaf:name "Chris".
AJamesPhillips commented 3 years ago

I don't understand RDF very well yet but I believe your storage is set there. See where it says space:storage crs:; I am very unconfident about this but I think the :; part means: "the value of this thing is the location of this file itself".

Try running this code and see if you get your pod URL (technically IRI):

https://github.com/centerofci/data-curator2/blob/main/app/frontend/src/sync/user_info/solid/get_solid_username.ts#L57

crspybits commented 3 years ago

Thanks, @AJamesPhillips for this!!

For me, getIri is the key method in your example. I don't know why, but for some reason, I'm not finding docs on that in the inrupt docs-- https://docs.inrupt.com/developer-tools/api/javascript/solid-client/ when I search for this term.

Of course, this not made easier as I'm not using Javascript. (I'm doing Swift on an iOS mobile client also Swift on a backend server). That said, it would be useful to me to know exactly what getIri is doing and how getSolidDataset and getThing work. I may need to recreate these or find a Swift library that does the same. I did find getSolidDataSet and getThing and will study those.

Also-- it looks like you are getting a webid from your user prior to the screen you show here: https://files.gitter.im/5b8fe069d73408ce4fa7007e/gLKW/image.png

I'm curious about how you got the OIDC provider. Is that something the user also enters before?

crspybits commented 3 years ago

I'm reading through https://www.w3.org/TR/turtle/ and am confused about the use of semi-colon line terminators in https://github.com/SyncServerII/ServerSolidAccount/issues/4#issuecomment-913840126.

jeff-zucker commented 3 years ago

space:storage crs:;

Things that have colons are prefixes which are all defined at the top. So where it it says "@prefix crs: </>." That means whenever you see crs: below, I mean </>. And </> means the root folder of the current host. <> means the current file location.

So the full statement is :

   <https://crspybits.solidcommunity.net/profile/card#me>
   <http://www.w3.org/ns/pim/space#storage>
   <https://crspybits.solidcommunity.net/>

In other words, your Pod root is https://crspybits.solidcommunity.net/.

crspybits commented 3 years ago

Parsing the https://github.com/SyncServerII/ServerSolidAccount/issues/4#issuecomment-913840126 result with Serd (I used https://github.com/crspybits/serd-parser, forked from https://github.com/kasei/serd-parser and updated for Swift 5.3).

//
//  RDFTestingApp.swift
//  RDFTesting
//
//  Created by Christopher G Prince on 9/6/21.
//

import SwiftUI
import SerdParser

@main
struct RDFTestingApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
            .onAppear() {
                testRDF_fromFile()
                testRDF_fromString()
            }
        }
    }

    func testRDF_fromFile() {
        // extract all foaf:name triples from a file.

        guard let testTTLFile = Bundle.main.url(forResource: "test", withExtension: "ttl") else {
            print("Could not get `test.ttl` URL")
            return
        }

        let parser = SerdParser()
        do {
            let count = try parser.parse(file: testTTLFile.path) { (s, p, o) in
                handleTriple(s: s, p: p, o: o)
            }

            print("\(count) triples processed")
        } catch let error {
            print("Error: \(error)")
        }
    }

    func testRDF_fromString() {
        // extract all foaf:name triples from a string.

        guard let testTTLFile = Bundle.main.url(forResource: "test", withExtension: "ttl") else {
            print("Could not get `test.ttl` URL")
            return
        }

        guard let data = try? Data(contentsOf: testTTLFile) else {
            print("Could not load data from `test.ttl` URL")
            return
        }

        guard let rtfString = String(data: data, encoding: .utf8) else {
            print("Could not load get string from data")
            return
        }

        let parser = SerdParser()
        do {
            let count = try parser.parse(string: rtfString) { (s, p, o) in
                handleTriple(s: s, p: p, o: o)
            }

            print("\(count) triples processed")
        } catch let error {
            print("Error: \(error)")
        }
    }

    func handleTriple(s: RDFTerm, p: RDFTerm, o: RDFTerm) {
        if case .iri("http://xmlns.com/foaf/0.1/name") = p {
            print("\(s) has name \(o) .")
        }
        else if case .iri("http://www.w3.org/ns/pim/space#storage") = p {
            print("storage: s: \(s); p: \(p): \(o)")
            let object: RDFTerm = o
            switch object {
            case .blank(let str):
                print("storage: blank: \(str)")
            case .datatype(let s1, let s2):
                print("storage: datatype: \(s1), \(s2)")
            case .iri(let str):
                print("storage: iri: \(str)")
            case .language(let s1, let s2):
                print("storage: language: \(s1), \(s2)")
            }
        }
    }
}

Output:

storage: s: <#me>; p: <http://www.w3.org/ns/pim/space#storage>: </>
storage: iri: /
<#me> has name "Chris" .
37 triples processed
storage: s: <#me>; p: <http://www.w3.org/ns/pim/space#storage>: </>
storage: iri: /
<#me> has name "Chris" .
37 triples processed

Not fully sure how to interpret these results. Should it be the host of https://crspybits.solidcommunity.net/profile/card#me?

crspybits commented 3 years ago

Let's see what happens with

MacBook-Pro-4:~ chris$ curl https://pod.inrupt.com/crspybits/profile/card#me

@prefix as: <https://www.w3.org/ns/activitystreams#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix acl: <http://www.w3.org/ns/auth/acl#> .
@prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix dc: <http://purl.org/dc/terms/> .
@prefix acp: <http://www.w3.org/ns/solid/acp#> .

<https://pod.inrupt.com/crspybits/profile/card>
        rdf:type           ldp:RDFSource ;
        rdf:type           ldp:RDFSource ;
        rdf:type           foaf:PersonalProfileDocument ;
        foaf:primaryTopic  <https://pod.inrupt.com/crspybits/profile/card#me> ;
        foaf:maker         <https://pod.inrupt.com/crspybits/profile/card#me> .

<https://pod.inrupt.com/crspybits/profile/card#me>
        <http://www.w3.org/ns/pim/space#preferencesFile>  <https://pod.inrupt.com/crspybits/settings/prefs.ttl> ;
        <http://www.w3.org/ns/solid/terms#account>  <https://pod.inrupt.com/crspybits/> ;
        <http://www.w3.org/ns/solid/terms#oidcIssuer>  <https://broker.pod.inrupt.com/> ;
        <http://www.w3.org/ns/solid/terms#privateTypeIndex>  <https://pod.inrupt.com/crspybits/settings/privateTypeIndex.ttl> ;
        rdf:type   foaf:Person ;
        <http://www.w3.org/ns/pim/space#storage>  <https://pod.inrupt.com/crspybits/> ;
        rdf:type   <http://schema.org/Person> ;
        foaf:name  "crspybits" ;
        ldp:inbox  <https://pod.inrupt.com/crspybits/inbox/> ;
        <http://www.w3.org/ns/solid/terms#publicTypeIndex>  <https://pod.inrupt.com/crspybits/settings/publicTypeIndex.ttl> .
crspybits commented 3 years ago

Running it with the same Swift code, I get:

storage: s: <https://pod.inrupt.com/crspybits/profile/card#me>; p: <http://www.w3.org/ns/pim/space#storage>: <https://pod.inrupt.com/crspybits/>
storage: iri: https://pod.inrupt.com/crspybits/
<https://pod.inrupt.com/crspybits/profile/card#me> has name "crspybits" .
15 triples processed
storage: s: <https://pod.inrupt.com/crspybits/profile/card#me>; p: <http://www.w3.org/ns/pim/space#storage>: <https://pod.inrupt.com/crspybits/>
storage: iri: https://pod.inrupt.com/crspybits/
<https://pod.inrupt.com/crspybits/profile/card#me> has name "crspybits" .
15 triples processed
crspybits commented 3 years ago

This looks promising!