bridgetown-webfinger 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +55 -0
- data/LICENSE.md +22 -0
- data/README.md +125 -0
- data/bridgetown-webfinger.gemspec +35 -0
- data/lib/bridgetown/webfinger/alias.rb +29 -0
- data/lib/bridgetown/webfinger/builder.rb +133 -0
- data/lib/bridgetown/webfinger/href.rb +29 -0
- data/lib/bridgetown/webfinger/initializer.rb +23 -0
- data/lib/bridgetown/webfinger/jrd.rb +164 -0
- data/lib/bridgetown/webfinger/link.rb +118 -0
- data/lib/bridgetown/webfinger/link_relation_type.rb +159 -0
- data/lib/bridgetown/webfinger/logging.rb +38 -0
- data/lib/bridgetown/webfinger/model.rb +13 -0
- data/lib/bridgetown/webfinger/parameters.rb +166 -0
- data/lib/bridgetown/webfinger/properties.rb +44 -0
- data/lib/bridgetown/webfinger/titles.rb +40 -0
- data/lib/bridgetown/webfinger/uri/acct.rb +138 -0
- data/lib/bridgetown/webfinger/version.rb +8 -0
- data/lib/bridgetown-webfinger.rb +49 -0
- data/lib/roda/plugins/bridgetown_webfinger.rb +149 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3c1086d7e337e4a0654b54bac8d38521cbe18c7502cfd816d8c6f164a40553b4
|
4
|
+
data.tar.gz: b014d6ffbb34b2b839af88730993d376d2dd1a4ff0ad43e51110eb397e03c6e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1dd3dbc3a4097dbeb8283cf3c92bc1a8d9eb9d88e3555a1f3a61a65a13f2a048f25e09d69c4e1c25def7d68748ac9c1f32a494a3ff314e812c463c27e9b3311a
|
7
|
+
data.tar.gz: e3452f05f6cedd25bf31e99752aedec2d30b4e747fe7f9e094995ad2909b7a89dccf38203b70b47288a1c07f802f69654b8f722ee95c7dd8f889283e1b6acc58
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.1.0](https://github.com/michaelherold/bridgetown-webfinger/tree/v0.1.0) - 2023-02-25
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Static file support for rendering a `.well-known/webfinger` file that always responds with the same information.
|
13
|
+
- Dynamic support via a Roda plugin for handling multiple authors and proper filtering for `rel` filters. This includes the ability to set `Access-Control-Allow-Origin` headers via the `allowed_hosts` option.
|
14
|
+
- Validation of all Webfinger properties and printed warnings for invalid values to help ensure you write interoperable Webfinger properties.
|
15
|
+
- An automation to quickly bootstrap your site's Webfinger capabilities.
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), we encourage **everyone** to help improve this project. Here are some ways _you_ can contribute:
|
4
|
+
|
5
|
+
* Use alpha, beta, and pre-release versions.
|
6
|
+
* Report bugs.
|
7
|
+
* Suggest new features.
|
8
|
+
* Write or edit documentation.
|
9
|
+
* Write specifications.
|
10
|
+
* Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace).
|
11
|
+
* Refactor code.
|
12
|
+
* Fix [issues].
|
13
|
+
* Review patches.
|
14
|
+
|
15
|
+
[issues]: https://github.com/michaelherold/bridgetown-webfinger/issues
|
16
|
+
|
17
|
+
## Submitting an Issue
|
18
|
+
|
19
|
+
We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure no one has already submitted it.
|
20
|
+
|
21
|
+
When submitting a bug report, please include a `<details>` block that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. This looks like the following:
|
22
|
+
|
23
|
+
```markdown
|
24
|
+
<details>
|
25
|
+
<summary>A description of the details block</summary>
|
26
|
+
|
27
|
+
All of the content that you want in here, perhaps with code fences. Note that
|
28
|
+
if you only have a code fence in here, you _must_ separate it from the <summary>
|
29
|
+
tag and the closing </details> or it won't render correctly.
|
30
|
+
|
31
|
+
Notice the empty line here ↓
|
32
|
+
|
33
|
+
</details>
|
34
|
+
```
|
35
|
+
|
36
|
+
Ideally, a bug report should include a pull request with failing specs.
|
37
|
+
|
38
|
+
## Submitting a Pull Request
|
39
|
+
|
40
|
+
1. Fork the repository.
|
41
|
+
2. Create a topic branch.
|
42
|
+
3. Add specs for your unimplemented feature or bug fix.
|
43
|
+
4. Run `bundle exec rake test`. If your specs pass, return to step 3.
|
44
|
+
5. Implement your feature or bug fix.
|
45
|
+
6. Run `bundle exec rake`. If your specs or any of the linters fail, return to step 5.
|
46
|
+
7. Open `coverage/index.html`. If your changes are not fully covered by your tests, return to step 3.
|
47
|
+
8. Add documentation for your feature or bug fix.
|
48
|
+
9. Commit and push your changes.
|
49
|
+
10. Submit a pull request.
|
50
|
+
|
51
|
+
## Tools to Help You Succeed
|
52
|
+
|
53
|
+
After checking out the repository, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
54
|
+
|
55
|
+
Before committing code, run `bundle exec rake` to check that the code conforms to the style guidelines of the project, that all of the tests are green (if you're writing a feature; if you're only submitting a failing test, then it does not have to pass!), and that you sufficiently documented the changes.
|
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2023-present
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Bridgetown Webfinger plugin
|
2
|
+
|
3
|
+
A [Bridgetown][1] plugin for handling [Webfinger][2] requests for the Fediverse and [IndieWeb][3].
|
4
|
+
|
5
|
+
Whether you run a single-author, statically rendered site or a multi-author site with a dynamic backend, this is your one-stop shop for Webfinger support.
|
6
|
+
|
7
|
+
This plugin allows for hosting Webfinger lookups on your website for [`acct:` URIs][4]. This is the first step to giving your site the ability to post to Fediverse apps like [Mastodon][5], [Pleroma][6], or [PeerTube][7]. While not all Fediverse apps use Webfinger, enough do that adding Webfinger support will be necessary to have your site participate in the Fediverse.
|
8
|
+
|
9
|
+
[1]: https://www.bridgetownrb.com
|
10
|
+
[2]: https://webfinger.net/
|
11
|
+
[3]: https://indieweb.org/
|
12
|
+
[4]: https://datatracker.ietf.org/doc/html/rfc7565
|
13
|
+
[5]: https://joinmastodon.org/
|
14
|
+
[6]: https://pleroma.social/
|
15
|
+
[7]: https://joinpeertube.org/
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Run this command to add this plugin to your site's Gemfile:
|
20
|
+
|
21
|
+
bundle add bridgetown-webfinger
|
22
|
+
|
23
|
+
Or you can use [an automation script][8] instead for guided setup:
|
24
|
+
|
25
|
+
bin/bt apply https://github.com/michaelherold/bridgetown-webfinger
|
26
|
+
|
27
|
+
[8]: https://www.bridgetownrb.com/docs/automations
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
This plugin runs in one of two modes: static mode or dynamic mode.
|
32
|
+
|
33
|
+
Static mode is for when you run a single-author, fully static website without using any of Bridgetown's Server-Side Rendering (SSR) capabilities. It is mildly non-compliant with the Webfinger specification, but is an accepted practice for static sites.
|
34
|
+
|
35
|
+
Dynamic mode uses a Roda plugin to serve author data for all accounts defined in your `src/_data/authors.yml` file.
|
36
|
+
|
37
|
+
### Authors data file
|
38
|
+
|
39
|
+
Within your authors data file, each author requires a `webfinger` associative array with data about the account. For example, for the account `bilbo`, who has a Twitter account at `https://twitter.com/bilbobaggins` and hosts an avatar at `https://bagend.com/bilbo.png`, the authors data file entry might look like:
|
40
|
+
|
41
|
+
```yaml
|
42
|
+
# src/_data/authors.yml
|
43
|
+
---
|
44
|
+
bilbo:
|
45
|
+
webfinger:
|
46
|
+
aliases:
|
47
|
+
- https://twitter.com/bilbobaggins
|
48
|
+
links:
|
49
|
+
- href: https://bagend.com/bilbo.png
|
50
|
+
rel: http://webfinger.net/rel/avatar
|
51
|
+
type: image/png
|
52
|
+
```
|
53
|
+
|
54
|
+
You may configure any number of authors in this way, however static mode works with only a single author.
|
55
|
+
|
56
|
+
### Static mode
|
57
|
+
|
58
|
+
To use static mode, configure your [authors data file](#authors-data-file) and add the following to your `config/initializers.rb` file:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# config/initializers.rb
|
62
|
+
Bridgetown.configure do
|
63
|
+
init "bridgetown-webfinger", static: true
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Bridgetown will now generate a `.well-known/webfinger` file using your first author's information when building the site.
|
68
|
+
|
69
|
+
#### Deploying to Netlify
|
70
|
+
|
71
|
+
To have the correct headers for your static file, add the following to your `netlify.toml`.
|
72
|
+
|
73
|
+
```toml
|
74
|
+
[[headers]]
|
75
|
+
for = "/.well-known/webfinger"
|
76
|
+
|
77
|
+
[headers.values]
|
78
|
+
Access-Control-Allow-Origin = "*"
|
79
|
+
Content-Type = "application/jrd+json"
|
80
|
+
```
|
81
|
+
|
82
|
+
See [the allowed hosts section below](#allowed-hosts) for more information about the access control header.
|
83
|
+
|
84
|
+
### Dynamic mode
|
85
|
+
|
86
|
+
To use dynamic mode, configure your [authors data file](#authors-data-file) and add the following to your `config/initializers.rb` file:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# config/initializers.rb
|
90
|
+
Bridgetown.configure do
|
91
|
+
init "bridgetown-webfinger", static: false, allowed_hosts: "*"
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
Then, add the `bridgetown_webfinger` plugin to your `RodaApp` and call the `bridgetown_webfinger` request method:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
# server/roda_app.rb
|
99
|
+
class RodaApp < Bridgetown::Rack::Roda
|
100
|
+
plugin :bridgetown_ssr # required
|
101
|
+
plugin :bridgetown_webfinger
|
102
|
+
|
103
|
+
route do |r|
|
104
|
+
r.bridgetown_webfinger
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
This generates a `.well-known/webfinger` route that serves `acct:` URI requests for authors on your site.
|
110
|
+
|
111
|
+
#### Allowed hosts
|
112
|
+
|
113
|
+
The [Webfinger specification][9] states that [servers must include the `Access-Control-Allow-Origin`][10] header to enable Cross-Origin Resource Sharing (CORS) requests and that they should use the least restrictive setting.
|
114
|
+
|
115
|
+
Unless you are hosting private Webfinger information — e.g. within a corporate network, to only authenticated followers, or for any other reason — you should set this to `"*"` using the configuration above.
|
116
|
+
|
117
|
+
If this is private information that you wish to restrict [set the header value appropriately][11].
|
118
|
+
|
119
|
+
[9]: https://datatracker.ietf.org/doc/html/rfc7033
|
120
|
+
[10]: https://datatracker.ietf.org/doc/html/rfc7033#section-5
|
121
|
+
[11]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
122
|
+
|
123
|
+
## Contributing
|
124
|
+
|
125
|
+
So you're interested in contributing to Bridgetown Webfinger? Check out our [contributing guidelines](CONTRIBUTING.md) for more information on how to do that.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/bridgetown/webfinger/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "bridgetown-webfinger"
|
7
|
+
spec.version = Bridgetown::Webfinger::VERSION
|
8
|
+
spec.author = "Michael Herold"
|
9
|
+
spec.email = "opensource@michaeljherold.com"
|
10
|
+
spec.summary = "Adds structured support for Webfinger to Bridgetown sites"
|
11
|
+
spec.homepage = "https://github.com/michaelherold/bridgetown-webfinger"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md]
|
15
|
+
spec.files += %w[bridgetown-webfinger.gemspec]
|
16
|
+
spec.files += Dir["lib/**/*.rb"]
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.required_ruby_version = ">= 3.0.0"
|
20
|
+
|
21
|
+
spec.add_dependency "bridgetown", ">= 1.2", "< 2.0"
|
22
|
+
spec.add_dependency "uri", ">= 0.12.0"
|
23
|
+
spec.add_dependency "zeitwerk"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", ">= 2"
|
26
|
+
|
27
|
+
spec.metadata = {
|
28
|
+
"bug_tracker_uri" => "https://github.com/michaelherold/bridgetown-webfinger/issues",
|
29
|
+
"changelog_uri" => "https://github.com/michaelherold/bridgetown-webfinger/blob/main/CHANGELOG.md",
|
30
|
+
"documentation_uri" => "https://rubydoc.info/gems/bridgetown-webfinger/#{Bridgetown::Webfinger::VERSION}",
|
31
|
+
"homepage_uri" => "https://github.com/michaelherold/bridgetown-webfinger",
|
32
|
+
"rubygems_mfa_required" => "true",
|
33
|
+
"source_code_uri" => "https://github.com/michaelherold/bridgetown-webfinger"
|
34
|
+
}
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bridgetown
|
4
|
+
module Webfinger
|
5
|
+
# Wraps an `alias` member within a {JRD}, which is a URI identifying the same subject
|
6
|
+
class Alias < Model
|
7
|
+
# Parses and maybe-returns an {Alias} when the value is one
|
8
|
+
#
|
9
|
+
# Aliases [must be URIs][1] so when the value is not a proper URI, it is
|
10
|
+
# ignored.
|
11
|
+
#
|
12
|
+
# [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.2
|
13
|
+
#
|
14
|
+
# @since 0.1.0
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
# @return [Alias, nil] an {Alias} when the value is a URI, nil otherwise
|
18
|
+
def self.parse(moniker)
|
19
|
+
if Webfinger.uri?(moniker)
|
20
|
+
new(moniker)
|
21
|
+
else
|
22
|
+
warn(
|
23
|
+
"Webfinger alias is malformed: #{moniker.inspect}, ignoring"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bridgetown
|
4
|
+
module Webfinger
|
5
|
+
# The [builder class][1] for working with Webfinger in Bridgetown
|
6
|
+
#
|
7
|
+
# [1]: https://www.bridgetownrb.com/docs/plugins#introduction-to-the-builder-api
|
8
|
+
class Builder < Bridgetown::Builder
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
# The hook method for enabling the builder's behavior
|
12
|
+
#
|
13
|
+
# @since 0.1.0
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# @return [void]
|
17
|
+
def build
|
18
|
+
generator :static_file
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Builds a static Webfinger file when correctly configured
|
24
|
+
#
|
25
|
+
# @since 0.1.0
|
26
|
+
# @api private
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def static_file
|
30
|
+
return unless config.webfinger.static
|
31
|
+
return unless (host = extract_site_host)
|
32
|
+
return unless (account, data = extract_authors_from_site_data)
|
33
|
+
|
34
|
+
site.add_generated_page StaticFile.new(site, account: account, data: data, host: host)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Extracts the site's host from its configuration
|
38
|
+
#
|
39
|
+
# @since 0.1.0
|
40
|
+
# @api private
|
41
|
+
#
|
42
|
+
# @return [String, nil] String when configured correctly, nil otherwise
|
43
|
+
def extract_site_host
|
44
|
+
host = site.config.url
|
45
|
+
|
46
|
+
if !host || host.empty?
|
47
|
+
warn(
|
48
|
+
"bridgetown-webfinger did not find your site's host configured at " \
|
49
|
+
"`site.config.url`; this is unsupported by static files so will be ignored"
|
50
|
+
)
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
uri = URI(host)
|
55
|
+
|
56
|
+
if uri.is_a?(URI::HTTP)
|
57
|
+
uri.host
|
58
|
+
elsif uri.instance_of?(URI::Generic)
|
59
|
+
uri.to_s
|
60
|
+
else
|
61
|
+
warn(
|
62
|
+
"bridgetown-webfinger detected a malformed host in your site configuration " \
|
63
|
+
"(#{uri} from `site.config.url`); this is unsupported and will be ignored"
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Extracts the author from the site's data
|
69
|
+
#
|
70
|
+
# @since 0.1.0
|
71
|
+
# @api private
|
72
|
+
#
|
73
|
+
# @return [HashWithDotAccess::Hash, nil] Hash when configured correctly, nil otherwise
|
74
|
+
def extract_authors_from_site_data
|
75
|
+
unless (authors = site.data.authors) && !authors.empty?
|
76
|
+
warn(
|
77
|
+
"bridgetown-webfinger did not detect any authors at `site.data.authors`; " \
|
78
|
+
"this is unsupported by static files so will be ignored"
|
79
|
+
)
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
83
|
+
unless authors.size == 1
|
84
|
+
warn(
|
85
|
+
"bridgetown-webfinger discovered multiple authors; this is unsupported " \
|
86
|
+
"by static files so will be ignored"
|
87
|
+
)
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
authors.first
|
92
|
+
end
|
93
|
+
|
94
|
+
# The static Webfinger file to serve
|
95
|
+
class StaticFile < Bridgetown::GeneratedPage
|
96
|
+
# Initializes a new static Webfinger file
|
97
|
+
#
|
98
|
+
# @since 0.1.0
|
99
|
+
# @api private
|
100
|
+
#
|
101
|
+
# @param site [Bridgetown::Site] the site object
|
102
|
+
# @param account [String] the account for the {URI::Acct}
|
103
|
+
# @param data [HashWithDotAccess::Hash] the data for the Webfinger JRD
|
104
|
+
# @param host [String] the host for the {URI::Acct}
|
105
|
+
# @return [void]
|
106
|
+
def initialize(site, account:, data:, host:)
|
107
|
+
super(site, __dir__, "/.well-known", "webfinger", from_plugin: true)
|
108
|
+
|
109
|
+
@subject = URI::Acct.build({account: account, host: host}).to_s
|
110
|
+
@jrd = JRD.parse(@subject, data.webfinger)
|
111
|
+
|
112
|
+
self.content = JSON.pretty_generate(@jrd.to_h)
|
113
|
+
end
|
114
|
+
|
115
|
+
# The {JRD} forming the data model for the static Webfinger page
|
116
|
+
#
|
117
|
+
# @since 0.1.0
|
118
|
+
# @api private
|
119
|
+
#
|
120
|
+
# @return [JRD] the JSON Resource Descriptor to serve for Webfinger
|
121
|
+
attr_reader :jrd
|
122
|
+
|
123
|
+
# The subject of the {JRD}
|
124
|
+
#
|
125
|
+
# @since 0.1.0
|
126
|
+
# @api private
|
127
|
+
#
|
128
|
+
# @return [String] the subject for the JRD to serve
|
129
|
+
attr_reader :subject
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bridgetown
|
4
|
+
module Webfinger
|
5
|
+
# Wraps an `href` member within a {Link}, which is the target URI
|
6
|
+
class Href < Model
|
7
|
+
# Parses and maybe-returns an {Href} when the value is one
|
8
|
+
#
|
9
|
+
# Hrefs [must be URIs][1] so when the value is not a proper URI, it is
|
10
|
+
# ignored.
|
11
|
+
#
|
12
|
+
# [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.4.3
|
13
|
+
#
|
14
|
+
# @since 0.1.0
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
# @return [Href, nil] an {Href} when the value is a URI, nil otherwise
|
18
|
+
def self.parse(href)
|
19
|
+
if Webfinger.uri?(href)
|
20
|
+
new(href)
|
21
|
+
else
|
22
|
+
warn(
|
23
|
+
"Webfinger link href is malformed: #{href.inspect}, ignoring"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @param config [Bridgetown::Configuration::ConfigurationDSL]
|
4
|
+
# @param allowed_origins [String] the value to use for the
|
5
|
+
# `Access-Control-Allow-Origin` header in the Roda plugin. Only override this
|
6
|
+
# if you're hosting private Webfinger data due to the [spec][1] saying, "servers
|
7
|
+
# SHOULD support the least restrictive setting"
|
8
|
+
# @param static [Boolean] whether to use static file rendering or not
|
9
|
+
#
|
10
|
+
# [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-5
|
11
|
+
Bridgetown.initializer "bridgetown-webfinger" do |config, allowed_origins: "*", static: true|
|
12
|
+
options = {allowed_origins: allowed_origins, static: static}
|
13
|
+
|
14
|
+
# :nocov: Because it's not possible to show coverage for both branches
|
15
|
+
if config.webfinger
|
16
|
+
config.webfinger Bridgetown::Utils.deep_merge_hashes(options, config.webfinger)
|
17
|
+
else
|
18
|
+
config.webfinger(options)
|
19
|
+
end
|
20
|
+
# :nocov:
|
21
|
+
|
22
|
+
config.builder Bridgetown::Webfinger::Builder
|
23
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bridgetown
|
4
|
+
module Webfinger
|
5
|
+
# A model of a [JSON Resource Descriptor][1]
|
6
|
+
#
|
7
|
+
# [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4
|
8
|
+
class JRD
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
# Parses and maybe-returns a {JRD} when the subject and data form one
|
12
|
+
#
|
13
|
+
# {JRD}s can describe any resource identifiable via a URI, whether you want
|
14
|
+
# to give public directory information about people via `acct:` URIs,
|
15
|
+
# copyright information about an article, or perhaps license information
|
16
|
+
# about a library.
|
17
|
+
#
|
18
|
+
# @since 0.1.0
|
19
|
+
# @api public
|
20
|
+
#
|
21
|
+
# @example Convert a Hash into a sanitized {JRD}
|
22
|
+
#
|
23
|
+
# Bridgetown::Webfinger::JRD.parse(
|
24
|
+
# "acct:bilbo@bagend.com",
|
25
|
+
# {
|
26
|
+
# aliases: ["https://bilbobaggins.com/"],
|
27
|
+
# links: [
|
28
|
+
# {
|
29
|
+
# href: "https://bagend.com/bilbo",
|
30
|
+
# rel: "http://webfinger.net/rel/profile-page",
|
31
|
+
# type: "text/html",
|
32
|
+
# properties: {
|
33
|
+
# "http://packetizer.com/ns/name" => "Bilbo @ Bag End"
|
34
|
+
# },
|
35
|
+
# titles: {
|
36
|
+
# "en-us" => "Bilbo Baggins's blog"
|
37
|
+
# }
|
38
|
+
# }
|
39
|
+
# ],
|
40
|
+
# properties: {
|
41
|
+
# "http://packetizer.com/ns/name" => "Bilbo Baggins"
|
42
|
+
# }
|
43
|
+
# }
|
44
|
+
# )
|
45
|
+
#
|
46
|
+
# @param subject [String] the (nominally) optional subject identified by
|
47
|
+
# the JRD; _should_ be present but is not _required_
|
48
|
+
# @param data [Hash] the data hash containing information to add to the
|
49
|
+
# JRD
|
50
|
+
# @return [JRD, nil] a {JRD} when the information is valid, nil otherwise
|
51
|
+
def self.parse(subject, data)
|
52
|
+
aliases = Array(data[:aliases]).filter_map { |moniker| Alias.parse(moniker) }
|
53
|
+
links = Array(data[:links]).filter_map { |link| Link.parse(link) }
|
54
|
+
properties = Properties.parse(data[:properties])
|
55
|
+
subject = Webfinger.uri?(subject) ? subject : nil
|
56
|
+
|
57
|
+
new(
|
58
|
+
aliases: (!aliases.empty?) ? aliases : nil,
|
59
|
+
links: (!links.empty?) ? links : nil,
|
60
|
+
properties: properties,
|
61
|
+
subject: subject
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a new {JRD}
|
66
|
+
#
|
67
|
+
# @since 0.1.0
|
68
|
+
# @api public
|
69
|
+
#
|
70
|
+
# @example Creating a new {JRD} without parsing a Hash
|
71
|
+
#
|
72
|
+
# Bridgetown::Webfinger::JRD.new(
|
73
|
+
# subject: "acct:bilbo@bagend.com",
|
74
|
+
# aliases: [Bridgetown::Webfinger::Alias.new("https://bilbobaggins.com/")]
|
75
|
+
# )
|
76
|
+
#
|
77
|
+
# @param aliases [Array<Alias>, nil] the optional {Alias}es for the resource
|
78
|
+
# @param links [Array<Link>, nil] the optional {Link}s related to the resource
|
79
|
+
# @param properties [Properties] the optional {Properties} describing the resource
|
80
|
+
# @param subject [String, nil] the (nominally) optional subject URI for the resource
|
81
|
+
def initialize(aliases: nil, links: nil, properties: nil, subject: nil)
|
82
|
+
@aliases = aliases
|
83
|
+
@links = links
|
84
|
+
@properties = properties
|
85
|
+
@subject = subject
|
86
|
+
end
|
87
|
+
|
88
|
+
# The list of {Alias}es for the resource
|
89
|
+
#
|
90
|
+
# @since 0.1.0
|
91
|
+
# @api public
|
92
|
+
#
|
93
|
+
# @example Read the {Alias}es for a {JRD}
|
94
|
+
#
|
95
|
+
# jrd = Bridgetown::Webfinger::JRD.parse(
|
96
|
+
# "acct:bilbo@bagend.com",
|
97
|
+
# {aliases: ["https://bilbobaggins.com"]}
|
98
|
+
# )
|
99
|
+
# jrd.aliases
|
100
|
+
#
|
101
|
+
# @return [Array<Alias>, nil] the optional {Alias}es for the resource
|
102
|
+
attr_reader :aliases
|
103
|
+
|
104
|
+
# The list of {Link}s for the resource
|
105
|
+
#
|
106
|
+
# @since 0.1.0
|
107
|
+
# @api public
|
108
|
+
#
|
109
|
+
# @example Read the {Link}s for a {JRD}
|
110
|
+
#
|
111
|
+
# jrd = Bridgetown::Webfinger::JRD.parse(
|
112
|
+
# "acct:bilbo@bagend.com",
|
113
|
+
# {links: [{href: "https://bilbobaggins.com", rel: "https://webfinger.net/rel/avatar"}]}
|
114
|
+
# )
|
115
|
+
# jrd.links
|
116
|
+
#
|
117
|
+
# @return [Array<Link>, nil] the optional {Link}s related to the resource
|
118
|
+
attr_reader :links
|
119
|
+
|
120
|
+
# The list of {Properties} for the resource
|
121
|
+
#
|
122
|
+
# @since 0.1.0
|
123
|
+
# @api public
|
124
|
+
#
|
125
|
+
# @example Read the {Properties} for a {JRD}
|
126
|
+
#
|
127
|
+
# jrd = Bridgetown::Webfinger::JRD.parse(
|
128
|
+
# "acct:bilbo@bagend.com",
|
129
|
+
# {properties: {"http://packetizer.com/ns/name" => "Bilbo Baggins"}}
|
130
|
+
# )
|
131
|
+
# jrd.properties
|
132
|
+
#
|
133
|
+
# @return [Properties, nil] the optional {Properties} describing the resource
|
134
|
+
attr_reader :properties
|
135
|
+
|
136
|
+
# The subject of the {JRD}
|
137
|
+
#
|
138
|
+
# @since 0.1.0
|
139
|
+
# @api public
|
140
|
+
#
|
141
|
+
# @example Read the subject for a {JRD}
|
142
|
+
#
|
143
|
+
# jrd = Bridgetown::Webfinger::JRD.parse("acct:bilbo@bagend.com")
|
144
|
+
# jrd.subject #=> "acct:bilbo@bagend.com"
|
145
|
+
#
|
146
|
+
# @return [String, nil] the (nominally) optional subject URI for the resource
|
147
|
+
attr_reader :subject
|
148
|
+
|
149
|
+
# Converts the {JRD} to a JSON-serializable Hash
|
150
|
+
#
|
151
|
+
# @since 0.1.0
|
152
|
+
# @api private
|
153
|
+
#
|
154
|
+
# @return [Hash] the JRD as a JSON-compatible Hash
|
155
|
+
def to_h
|
156
|
+
result = {subject: subject}
|
157
|
+
result[:aliases] = aliases if aliases
|
158
|
+
result[:links] = links.map(&:to_h) if links
|
159
|
+
result[:properties] = properties.to_h if properties
|
160
|
+
result
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|