bridgetown-webfinger 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|