Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add well known handlers API #24702

Merged
merged 1 commit into from
Dec 18, 2020
Merged

Conversation

ChristophWurst
Copy link
Member

@ChristophWurst ChristophWurst commented Dec 14, 2020

RFC5785 defines the .well-known/ URL format that is used by some services to let software detect services on remote host. Now that is also helpful for us, given how federated Nextcloud is. We have some hard-coded well known URLs for DAV in the .htaccess and the recommended webserver config right now, be we can also just handle those requests ourselves.

The services discoverable via this mechanism are broad, hence we can put this into an API and let apps provide the implementations.

API

Apps can now interact with requests to the well-known endpoint(s) by registering a handler via the registration context. The handler must implement a single method that gets the service name (e.g. webfinger, a context object and the optional response of the previous handler). The previous response is passed because all apps get a chance to react to the request, and in the case of JRD responses (webfinger, nodeinfo, etc), it is even required that a handler can add to the response of a previous handler. The context object offers access to the HTTP request, e.g. to fetch URL parameters. I opted for a context object over the request as direct parameter so we can add other context properties later on without a breaking change on all handler implementations.

Handlers return an IResponse that currently has two implementations: one for JRD and a generic one. The JRD is used for webfinger and nodeinfo. The generic one accepts any HTTP response, so this also work with formats other than JRD or even JSON. https://en.wikipedia.org/wiki/List_of_/.well-known/_services_offered_by_webservers shows that there are some services that don't use JSON, so that opens the door for an app to make use of this without limitations.

Example implementation: nextcloud/social#1163

Internal handling

The well-known URL is caught via a global route. The service suffix is a route segment. This gets wired to an app framework router, that then delegates to a manager. The manager asks the bootstrap coordinator for registered handlers, loads them via the DI container and then all handlers are called consecutively, while the latter can modify/overwrite the previous handler's response.

This is the result of some discussion about #21817 (review). I didn't have much time to think about the design and I'm sure I missed a few things. Tests will be added once the design is settled.

Testing

You can send simple curl requests like curl -k -v https://nextcloud/.well-known/webfinger?resource=acct%3Aalice%40example.com. If the social app is set up, it should be able to respond to those. Due to a lack of a working social app locally I could only test this with some hard coded values.

To see how it handles any non-handled requests send curl -k -v https://nextcloud/.well-known/this-is-the-wurst and you'll get a 404.

Todo

@ChristophWurst ChristophWurst added the pending documentation This pull request needs an associated documentation update label Dec 15, 2020
@ChristophWurst ChristophWurst force-pushed the enhancement/well-known-handler-api branch 2 times, most recently from c56fc2f to 3a3dbe4 Compare December 15, 2020 13:40
@ChristophWurst ChristophWurst added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Dec 15, 2020
@ChristophWurst ChristophWurst marked this pull request as ready for review December 15, 2020 13:40
@ArtificialOwl
Copy link
Member

Some comments:

  • JrdResponse() should be initiated within Core, with the right $subject. The main reason is that it is cleaner, as subject IS always the value of the param resource and resource is mandatory in the request. The other reason is that someone, one day, will have his app working perfectly without initiating himself the $subject because another app is already doing it. And that app will become dependent of that other app.

  • the response to the client should not contains empty entries:

  "expires": null,
  "aliases": [],
  "properties": [],
  • In the tests, you are initiating a JrdResponse()with the subject 'webfinger'. webfinger is the service, not the subject.

  • We should define a const somewhere with the value of a resource that will be used to retrieve local services running on the Nextcloud. A simple one one I have is mind is http://nextcloud.com/:

curl -X GET "https://cloud.example.net/.well-known/webfinger?resource=http://nextcloud.com/"
{
  "subject": "http://nextcloud.com/",
  "links": [
    {
      "rel": "https://apps.nextcloud.com/apps/circles",
      "type": "application/json",
      "href": "https://cloud.example.net/index.php/apps/circles/",
      "properties": {
        "app": "circles",
        "name": "Circles",
        "version": "21.1.2"
      }
    }
  ]
}
  • The RFC also speaks about having the param rel within the request (https://tools.ietf.org/html/rfc7033#section-4.3). Now, again, might be nice to have the filter in the Core, instead of asking each app to implement a check on the param rel. The real blocker is that I don't think IRequest manage multiple identical parameter in a GET request:
    The "rel" parameter MAY be included multiple times in order to request multiple link relation types.

@ChristophWurst
Copy link
Member Author

JrdResponse() should be initiated within Core, with the right $subject. The main reason is that it is cleaner, as subject IS always the value of the param resource and resource is mandatory in the request. The other reason is that someone, one day, will have his app working perfectly without initiating himself the $subject because another app is already doing it. And that app will become dependent of that other app.

We can initialize it. Again, that puts us into the mindset that it's all just about webfinger, which isn't the case. But it's an acceptable tradeoff that we can revert later without breaking the API. I'll pass it 👍.

the response to the client should not contains empty entries:

We should define a const somewhere with the value of a resource that will be used to retrieve local services running on the Nextcloud. A simple one one I have is mind is http://nextcloud.com/

What do we need this for any do we need this for the API design? Sounds like a nice to have and certainly doable in a follow-up.

The RFC also speaks about having the param rel within the request (https://tools.ietf.org/html/rfc7033#section-4.3). Now, again, might be nice to have the filter in the Core, instead of asking each app to implement a check on the param rel. The real blocker is that I don't think IRequest manage multiple identical parameter in a GET request

That is what the RFC says, but it's a complex topic. There is no standard that defines how those parameters should look like in the URL. That might explain why our request object currently doesn't handle them. See https://stackoverflow.com/a/24728298/2239067 and the linked documents.

@ChristophWurst ChristophWurst force-pushed the enhancement/well-known-handler-api branch from 3a3dbe4 to dd01db6 Compare December 15, 2020 19:12
@ArtificialOwl

This comment has been minimized.

@ChristophWurst

This comment has been minimized.

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
@ChristophWurst ChristophWurst force-pushed the enhancement/well-known-handler-api branch from dd01db6 to 6995223 Compare December 16, 2020 12:13
Copy link
Member

@juliusknorr juliusknorr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense and the API looks sane 👍

@ArtificialOwl
Copy link
Member

Still have issue with the getParam(), but it seems to be local. I will try tonight on a fresh install from master, and not a patched nc21beta1. Subject is not initiated here, because of this issue, but the code looks good enough.

The RFC 6415 seems to ask for XRD for host-meta, not sure we want to implement this today and just keep host-meta.json for a first merge ?

The rest looks good.

@ChristophWurst
Copy link
Member Author

ChristophWurst commented Dec 17, 2020

Still have issue with the getParam(), but it seems to be local. I will try tonight on a fresh install from master, and not a patched nc21beta1. Subject is not initiated here, because of this issue, but the code looks good enough.

Sounds good. We can address this independent of this new API PR. A bugfix is always easy to get in and also backported.

The RFC 6415 seems to ask for XRD for host-meta, not sure we want to implement this today and just keep host-meta.json for a first merge ?

Where is the current host-meta(.json)? I couldn't find it.

We can add an XrdResponse if necessary. Alternatively the host-meta handlers can already return a GenericResponse that wraps a custom \OCP\AppFramework\Http\Response that renders the XRD format.

@ChristophWurst
Copy link
Member Author

The open items can be addressed in follow-ups (in 22). I'm merging this so we get the basic API in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants