├── .codeclimate.yml ├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── lint.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── .yardopts ├── CONTRIBUTING.md ├── COPYRIGHT ├── Changelog.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── app └── controllers │ └── diaspora_federation │ ├── application_controller.rb │ ├── fetch_controller.rb │ ├── h_card_controller.rb │ ├── receive_controller.rb │ └── webfinger_controller.rb ├── bin ├── bundle ├── pronto ├── rails ├── rake ├── rspec └── rubocop ├── config ├── initializers │ ├── filter_parameter_logging.rb │ └── mime_types.rb └── routes.rb ├── diaspora_federation-json_schema.gemspec ├── diaspora_federation-rails.gemspec ├── diaspora_federation-test.gemspec ├── diaspora_federation.gemspec ├── docs ├── _config.yml ├── _entities │ ├── account_deletion.md │ ├── account_migration.md │ ├── comment.md │ ├── contact.md │ ├── conversation.md │ ├── embed.md │ ├── event.md │ ├── event_participation.md │ ├── like.md │ ├── location.md │ ├── message.md │ ├── participation.md │ ├── photo.md │ ├── poll.md │ ├── poll_answer.md │ ├── poll_participation.md │ ├── post.md │ ├── profile.md │ ├── reshare.md │ ├── retraction.md │ └── status_message.md ├── _includes │ ├── active_class.html │ ├── discovery_tree.html │ ├── entity_tree.html │ ├── federation_tree.html │ ├── top_navbar.html │ └── warning_box.html ├── _layouts │ └── default.html ├── assets │ ├── css │ │ ├── _pygments.scss │ │ ├── _theme.scss │ │ ├── _webfonts.scss │ │ └── main.scss │ ├── fonts │ │ ├── FiraMono-Bold.eot │ │ ├── FiraMono-Bold.ttf │ │ ├── FiraMono-Bold.woff │ │ ├── FiraMono-Regular.eot │ │ ├── FiraMono-Regular.ttf │ │ ├── FiraMono-Regular.woff │ │ ├── FiraSans-Bold.eot │ │ ├── FiraSans-Bold.ttf │ │ ├── FiraSans-Bold.woff │ │ ├── FiraSans-BoldItalic.eot │ │ ├── FiraSans-BoldItalic.ttf │ │ ├── FiraSans-BoldItalic.woff │ │ ├── FiraSans-Italic.eot │ │ ├── FiraSans-Italic.ttf │ │ ├── FiraSans-Italic.woff │ │ ├── FiraSans-Regular.eot │ │ ├── FiraSans-Regular.ttf │ │ └── FiraSans-Regular.woff │ ├── images │ │ └── favicon.ico │ └── vendor │ │ ├── css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.min.js │ │ └── jquery-2.2.0.min.js ├── discovery │ ├── hcard.md │ └── webfinger.md ├── entities │ └── index.md ├── federation │ ├── diaspora_scheme.md │ ├── encryption.md │ ├── fetching.md │ ├── magicsig.md │ ├── relayable.md │ ├── routes.md │ ├── types.md │ └── xml_serialization.md ├── index.md └── schemas │ └── federation_entities.json ├── lib ├── diaspora_federation.rb ├── diaspora_federation │ ├── callbacks.rb │ ├── discovery.rb │ ├── discovery │ │ ├── discovery.rb │ │ ├── exceptions.rb │ │ ├── h_card.rb │ │ ├── web_finger.rb │ │ └── xrd_document.rb │ ├── engine.rb │ ├── entities.rb │ ├── entities │ │ ├── account_deletion.rb │ │ ├── account_migration.rb │ │ ├── account_migration │ │ │ └── signable.rb │ │ ├── comment.rb │ │ ├── contact.rb │ │ ├── conversation.rb │ │ ├── embed.rb │ │ ├── event.rb │ │ ├── event_participation.rb │ │ ├── like.rb │ │ ├── location.rb │ │ ├── message.rb │ │ ├── participation.rb │ │ ├── person.rb │ │ ├── photo.rb │ │ ├── poll.rb │ │ ├── poll_answer.rb │ │ ├── poll_participation.rb │ │ ├── post.rb │ │ ├── profile.rb │ │ ├── related_entity.rb │ │ ├── relayable.rb │ │ ├── reshare.rb │ │ ├── retraction.rb │ │ ├── signable.rb │ │ └── status_message.rb │ ├── entity.rb │ ├── federation.rb │ ├── federation │ │ ├── diaspora_url_parser.rb │ │ ├── fetcher.rb │ │ ├── receiver.rb │ │ ├── receiver │ │ │ ├── abstract_receiver.rb │ │ │ ├── exceptions.rb │ │ │ ├── private.rb │ │ │ └── public.rb │ │ ├── sender.rb │ │ └── sender │ │ │ └── hydra_wrapper.rb │ ├── http_client.rb │ ├── logging.rb │ ├── parsers.rb │ ├── parsers │ │ ├── base_parser.rb │ │ ├── json_parser.rb │ │ ├── relayable_json_parser.rb │ │ ├── relayable_xml_parser.rb │ │ └── xml_parser.rb │ ├── properties_dsl.rb │ ├── rails.rb │ ├── salmon.rb │ ├── salmon │ │ ├── aes.rb │ │ ├── encrypted_magic_envelope.rb │ │ ├── exceptions.rb │ │ └── magic_envelope.rb │ ├── schemas.rb │ ├── schemas │ │ └── federation_entities.json │ ├── test.rb │ ├── test │ │ ├── entity_generator.rb │ │ └── factories.rb │ ├── validators.rb │ ├── validators │ │ ├── account_deletion_validator.rb │ │ ├── account_migration_validator.rb │ │ ├── comment_validator.rb │ │ ├── contact_validator.rb │ │ ├── conversation_validator.rb │ │ ├── embed_validator.rb │ │ ├── event_participation_validator.rb │ │ ├── event_validator.rb │ │ ├── h_card_validator.rb │ │ ├── like_validator.rb │ │ ├── location_validator.rb │ │ ├── message_validator.rb │ │ ├── optional_aware_validator.rb │ │ ├── participation_validator.rb │ │ ├── person_validator.rb │ │ ├── photo_validator.rb │ │ ├── poll_answer_validator.rb │ │ ├── poll_participation_validator.rb │ │ ├── poll_validator.rb │ │ ├── profile_validator.rb │ │ ├── related_entity_validator.rb │ │ ├── relayable_validator.rb │ │ ├── reshare_validator.rb │ │ ├── retraction_validator.rb │ │ ├── rules │ │ │ ├── birthday.rb │ │ │ ├── boolean.rb │ │ │ ├── diaspora_id.rb │ │ │ ├── diaspora_id_list.rb │ │ │ ├── guid.rb │ │ │ ├── not_nil.rb │ │ │ ├── public_key.rb │ │ │ └── tag_count.rb │ │ ├── status_message_validator.rb │ │ └── web_finger_validator.rb │ └── version.rb └── tasks │ └── build.rake ├── spec ├── controllers │ └── diaspora_federation │ │ ├── application_controller_spec.rb │ │ ├── fetch_controller_spec.rb │ │ ├── h_card_controller_spec.rb │ │ ├── receive_controller_spec.rb │ │ └── webfinger_controller_spec.rb ├── entities.rb ├── factories.rb ├── integration │ └── comment_integration_spec.rb ├── lib │ ├── diaspora_federation │ │ ├── callbacks_spec.rb │ │ ├── discovery │ │ │ ├── discovery_spec.rb │ │ │ ├── h_card_spec.rb │ │ │ ├── web_finger_spec.rb │ │ │ └── xrd_document_spec.rb │ │ ├── entities │ │ │ ├── account_deletion_spec.rb │ │ │ ├── account_migration │ │ │ │ └── signable_spec.rb │ │ │ ├── account_migration_spec.rb │ │ │ ├── comment_spec.rb │ │ │ ├── contact_spec.rb │ │ │ ├── conversation_spec.rb │ │ │ ├── embed_spec.rb │ │ │ ├── event_participation_spec.rb │ │ │ ├── event_spec.rb │ │ │ ├── like_spec.rb │ │ │ ├── location_spec.rb │ │ │ ├── message_spec.rb │ │ │ ├── participation_spec.rb │ │ │ ├── person_spec.rb │ │ │ ├── photo_spec.rb │ │ │ ├── poll_answer_spec.rb │ │ │ ├── poll_participation_spec.rb │ │ │ ├── poll_spec.rb │ │ │ ├── profile_spec.rb │ │ │ ├── related_entity_spec.rb │ │ │ ├── relayable_spec.rb │ │ │ ├── reshare_spec.rb │ │ │ ├── retraction_spec.rb │ │ │ ├── signable_spec.rb │ │ │ └── status_message_spec.rb │ │ ├── entity_spec.rb │ │ ├── federation │ │ │ ├── diaspora_url_parser_spec.rb │ │ │ ├── fetcher_spec.rb │ │ │ ├── receiver │ │ │ │ ├── private_spec.rb │ │ │ │ └── public_spec.rb │ │ │ ├── receiver_spec.rb │ │ │ ├── sender │ │ │ │ └── hydra_wrapper_spec.rb │ │ │ └── sender_spec.rb │ │ ├── http_client_spec.rb │ │ ├── parsers │ │ │ ├── base_parser_spec.rb │ │ │ ├── json_parser_spec.rb │ │ │ ├── relayable_json_parser_spec.rb │ │ │ ├── relayable_xml_parser_spec.rb │ │ │ └── xml_parser_spec.rb │ │ ├── properties_dsl_spec.rb │ │ ├── salmon │ │ │ ├── aes_spec.rb │ │ │ ├── encrypted_magic_envelope_spec.rb │ │ │ └── magic_envelope_spec.rb │ │ ├── schemas_spec.rb │ │ └── validators │ │ │ ├── account_deletion_validator_spec.rb │ │ │ ├── account_migration_validator_spec.rb │ │ │ ├── comment_validator_spec.rb │ │ │ ├── contact_validator_spec.rb │ │ │ ├── conversation_validator_spec.rb │ │ │ ├── embed_validator_spec.rb │ │ │ ├── event_participation_validator_spec.rb │ │ │ ├── event_validator_spec.rb │ │ │ ├── h_card_validator_spec.rb │ │ │ ├── like_validator_spec.rb │ │ │ ├── location_validator_spec.rb │ │ │ ├── message_validator_spec.rb │ │ │ ├── optional_aware_validator_spec.rb │ │ │ ├── participation_validator_spec.rb │ │ │ ├── person_validator_spec.rb │ │ │ ├── photo_validator_spec.rb │ │ │ ├── poll_answer_validator_spec.rb │ │ │ ├── poll_participation_validator_spec.rb │ │ │ ├── poll_validator_spec.rb │ │ │ ├── profile_validator_spec.rb │ │ │ ├── related_entity_validator_spec.rb │ │ │ ├── reshare_validator_spec.rb │ │ │ ├── retraction_validator_spec.rb │ │ │ ├── rules │ │ │ ├── birthday_spec.rb │ │ │ ├── boolean_spec.rb │ │ │ ├── diaspora_id_list_spec.rb │ │ │ ├── diaspora_id_spec.rb │ │ │ ├── guid_spec.rb │ │ │ ├── not_nil_spec.rb │ │ │ ├── public_key_spec.rb │ │ │ └── tag_count_spec.rb │ │ │ ├── status_message_validator_spec.rb │ │ │ └── web_finger_validator_spec.rb │ └── diaspora_federation_spec.rb ├── routing │ ├── fetch_routing_spec.rb │ ├── receive_routing_spec.rb │ └── webfinger_routing_spec.rb ├── spec_helper.rb └── support │ ├── fixture_builder.rb │ ├── helper_methods.rb │ ├── shared_entity_specs.rb │ ├── shared_magic_envelope_specs.rb │ ├── shared_parser_specs.rb │ ├── shared_signable_specs.rb │ └── shared_validator_specs.rb └── test ├── dummy ├── README.rdoc ├── Rakefile ├── app │ └── models │ │ ├── entity.rb │ │ └── person.rb ├── bin │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── diaspora_federation.rb │ │ ├── filter_parameter_logging.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── routes.rb │ └── secrets.yml └── log │ └── .keep └── scripts └── ci.sh /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | rubocop: 4 | enabled: true 5 | channel: rubocop-1-22-3 6 | bundler-audit: 7 | enabled: true 8 | ratings: 9 | paths: 10 | - app/** 11 | - config/** 12 | - lib/** 13 | - spec/** 14 | - test/** 15 | - Gemfile.lock 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | - main 7 | - master 8 | pull_request: 9 | 10 | jobs: 11 | test: 12 | name: 'Ruby: ${{ matrix.ruby }}, Rails: ${{ matrix.rails }}' 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ruby: 18 | - "3.2" 19 | - "3.1" 20 | - "3.0" 21 | - "2.7" 22 | rails: 23 | - "7.0" 24 | - "6.1" 25 | - "6.0" 26 | - "5.2" 27 | - "none" 28 | exclude: 29 | - ruby: "3.2" 30 | rails: "5.2" 31 | - ruby: "3.1" 32 | rails: "5.2" 33 | - ruby: "3.0" 34 | rails: "5.2" 35 | env: 36 | RAILS_VERSION: ${{ matrix.rails }} 37 | BUNDLE_WITHOUT: development 38 | BUNDLE_DISABLE_SHARED_GEMS: true 39 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} 40 | steps: 41 | - uses: actions/checkout@v3 42 | - name: Delete Gemfile.lock 43 | run: rm Gemfile.lock 44 | if: matrix.rails != '7.0' # Gemfile.lock is only generated for latest rails version 45 | - uses: ruby/setup-ruby@v1 46 | with: 47 | ruby-version: ${{ matrix.ruby }} 48 | bundler-cache: true 49 | - name: Run tests 50 | run: test/scripts/ci.sh 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | vendor/bundle 3 | pkg/ 4 | 5 | # IDE files 6 | .idea 7 | 8 | # coverage reports 9 | coverage 10 | 11 | # documentation 12 | rdoc 13 | doc 14 | .yardoc/ 15 | 16 | # Temporary files 17 | .DS_Store 18 | *.swp 19 | *~ 20 | *.directory 21 | 22 | # dummmy app 23 | test/dummy/log/*.log 24 | test/dummy/log/*.log* 25 | test/dummy/tmp 26 | 27 | rspec-persistence.txt 28 | 29 | .rake_tasks 30 | 31 | # jekyll 32 | docs/_site 33 | docs/.sass-cache 34 | docs/.jekyll-metadata 35 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format Fuubar 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | diaspora-federation 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --quiet 2 | - 3 | Changelog.md 4 | CONTRIBUTING.md 5 | LICENSE 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to our federation 2 | 3 | First of all: thank you very much for helping us out! Since the federation layer is a very crucial part of diaspora\*, contributing has some rules, depending on what you want to contribute. 4 | 5 | ## Things you need to know before contributing 6 | 7 | If you want to get in touch with other diaspora\* developers, [check our wiki][how-we-communicate] for information on how we communicate. Feel free to ask if you have any questions! 8 | 9 | As with the main diaspora\* repository, everyone interacting with our code, issue trackers, chat rooms, mailing lists, the wiki, and the discourse forum is expected to follow the [diaspora\* code of conduct][code-of-conduct]. 10 | 11 | ## Report a security issue 12 | 13 | Found a security issue in some parts of the protocol or its implementation? Please disclose it responsibly. We have a team of developers listening to [security@diasporafoundation.org][sec-mail]. The PGP fingerprint is [AB0D AB02 0FC5 D398 03AB 3CE1 6F70 243F 27AD 886A][pgp]. 14 | 15 | ## Improve the documentation 16 | 17 | Feel free to open a pull request right away if you would like to improve the inlined code documentation or the dedicated federation protocol documentation. 18 | 19 | ## Changing implementations or altering fields 20 | 21 | If you want to change implementations of existing functions or you would like to suggest a change in the federation protocol, please open an issue *before doing anything else*. Try to describe your idea and why you think the implementation or the protocol should change as close as possible. Most changes will have to get discussed in a larger group before implementing them, so it's important to start those discussions before writing any code. 22 | 23 | [code-of-conduct]: https://github.com/diaspora/diaspora/blob/develop/CODE_OF_CONDUCT.md 24 | [how-we-communicate]: https://wiki.diasporafoundation.org/How_we_communicate 25 | [pgp]: https://pgp.mit.edu/pks/lookup?op=get&search=0x6F70243F27AD886A 26 | [sec-mail]: mailto:security@diasporafoundation.org 27 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | diaspora* federation library 2 | Copyright (C) 2015 Benjamin Neff 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as 6 | published by the Free Software Foundation, either version 3 of the 7 | License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | Some parts are based on an older federation gem from Florian Staudacher: 15 | https://github.com/Raven24/diaspora-federation 16 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require "bundler/setup" 5 | rescue LoadError 6 | puts "You must `gem install bundler` and `bundle install` to run rake tasks" 7 | end 8 | 9 | require "rdoc/task" 10 | 11 | RDoc::Task.new(:rdoc) do |rdoc| 12 | rdoc.rdoc_dir = "rdoc" 13 | rdoc.title = "DiasporaFederation" 14 | rdoc.options << "--line-numbers" 15 | rdoc.rdoc_files.include("lib/**/*.rb") 16 | end 17 | 18 | if defined?(Rails) 19 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 20 | load "rails/tasks/engine.rake" 21 | load "rails/tasks/statistics.rake" 22 | 23 | Rails.application.load_tasks 24 | else 25 | require "rspec/core/rake_task" 26 | RSpec::Core::RakeTask.new(:spec) 27 | FileList["lib/tasks/**/*.rake"].each {|task| load(task) } 28 | end 29 | 30 | Bundler::GemHelper.install_tasks name: "diaspora_federation" 31 | 32 | desc "Run all tests" 33 | task test: :spec 34 | task default: :test 35 | -------------------------------------------------------------------------------- /app/controllers/diaspora_federation/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # Base controller for all DiasporaFederation controllers 5 | class ApplicationController < ActionController::Base 6 | before_action :set_locale 7 | 8 | # Fix locale leakage from other requests. 9 | # Set "en" as locale for every federation request. 10 | def set_locale 11 | I18n.locale = :en 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/diaspora_federation/fetch_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency "diaspora_federation/application_controller" 4 | 5 | module DiasporaFederation 6 | # This controller processes fetch requests. 7 | class FetchController < ApplicationController 8 | # Returns the fetched entity or a redirect 9 | # 10 | # GET /fetch/:type/:guid 11 | def fetch 12 | entity = fetch_public_entity 13 | if entity 14 | magic_env = create_magic_envelope(entity) 15 | if magic_env 16 | render xml: magic_env, content_type: "application/magic-envelope+xml" 17 | else 18 | redirect_url = DiasporaFederation.callbacks.trigger(:fetch_person_url_to, 19 | entity.author, "/fetch/#{params[:type]}/#{params[:guid]}") 20 | redirect_to redirect_url, allow_other_host: true 21 | end 22 | else 23 | head :not_found 24 | end 25 | end 26 | 27 | private 28 | 29 | def fetch_public_entity 30 | type = DiasporaFederation::Entity.entity_class(params[:type]).to_s.rpartition("::").last 31 | DiasporaFederation.callbacks.trigger(:fetch_public_entity, type, params[:guid]) 32 | end 33 | 34 | def create_magic_envelope(entity) 35 | privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, entity.author) 36 | Salmon::MagicEnvelope.new(entity, entity.author).envelop(privkey) if privkey 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/diaspora_federation/h_card_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency "diaspora_federation/application_controller" 4 | 5 | module DiasporaFederation 6 | # This controller generates the hcard. 7 | class HCardController < ApplicationController 8 | # Returns the hcard of the user 9 | # 10 | # GET /hcard/users/:guid 11 | def hcard 12 | person_hcard = DiasporaFederation.callbacks.trigger(:fetch_person_for_hcard, params[:guid]) 13 | 14 | if person_hcard.nil? 15 | head :not_found 16 | else 17 | logger.info "hcard profile request for: #{person_hcard.nickname}:#{person_hcard.guid}" 18 | # rubocop:disable Rails/OutputSafety 19 | render html: person_hcard.to_html.html_safe 20 | # rubocop:enable Rails/OutputSafety 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/diaspora_federation/receive_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency "diaspora_federation/application_controller" 4 | 5 | module DiasporaFederation 6 | # This controller processes receiving messages. 7 | class ReceiveController < ApplicationController 8 | skip_forgery_protection 9 | 10 | # Receives public messages 11 | # 12 | # POST /receive/public 13 | def public 14 | data = request.body.read 15 | logger.debug data 16 | 17 | DiasporaFederation.callbacks.trigger(:queue_public_receive, data) 18 | 19 | head :accepted 20 | end 21 | 22 | # Receives private messages for a user 23 | # 24 | # POST /receive/users/:guid 25 | def private 26 | data = request.body.read 27 | logger.debug data 28 | 29 | success = DiasporaFederation.callbacks.trigger(:queue_private_receive, params[:guid], data) 30 | 31 | head success ? :accepted : :not_found 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'bundler' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | 16 | load Gem.bin_path("bundler", "bundle") 17 | -------------------------------------------------------------------------------- /bin/pronto: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'pronto' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("pronto", "pronto") 18 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path("..", __dir__) 6 | ENGINE_PATH = File.expand_path("../lib/diaspora_federation/engine", __dir__) 7 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 11 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 12 | 13 | require "rails/all" 14 | require "rails/engine/commands" 15 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'rake' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("rake", "rake") 18 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'rspec' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("rspec-core", "rspec") 18 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'rubocop' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("rubocop", "rubocop") 18 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Don't log received xml data. 4 | Rails.application.config.filter_parameters += %i[xml aes_key encrypted_magic_envelope] 5 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # mime types for webfinger 4 | Mime::Type.register "application/jrd+json", :jrd 5 | Mime::Type.register "application/xrd+xml", :xrd 6 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | DiasporaFederation::Engine.routes.draw do 4 | controller :receive do 5 | post "receive/public" => :public, :as => "receive_public" 6 | post "receive/users/:guid" => :private, :as => "receive_private" 7 | end 8 | 9 | controller :fetch do 10 | get "fetch/:type/:guid" => :fetch, :as => "fetch", :guid => /#{Validation::Rule::Guid::VALID_CHARS}/ 11 | end 12 | 13 | controller :webfinger do 14 | get ".well-known/webfinger" => :webfinger, :as => "webfinger" 15 | end 16 | 17 | controller :h_card do 18 | get "hcard/users/:guid" => :hcard, :as => "hcard" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /diaspora_federation-json_schema.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("lib", __dir__) 4 | 5 | # Maintain your gem's version: 6 | require "diaspora_federation/version" 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = "diaspora_federation-json_schema" 11 | s.version = DiasporaFederation::VERSION 12 | s.authors = ["Benjamin Neff", "cmrd Senya"] 13 | s.email = ["benjamin@coding4.coffee", "senya@riseup.net"] 14 | s.homepage = "https://github.com/diaspora/diaspora_federation" 15 | s.summary = "diaspora* federation json schemas" 16 | s.description = "This gem provides JSON schemas (currently one schema) for " \ 17 | "validating JSON serialized federation objects." 18 | s.license = "AGPL-3.0" 19 | s.metadata = { 20 | "rubygems_mfa_required" => "true" 21 | } 22 | 23 | s.files = Dir["lib/diaspora_federation/schemas.rb", "lib/diaspora_federation/schemas/*.json"] 24 | 25 | s.required_ruby_version = ">= 2.7" 26 | end 27 | -------------------------------------------------------------------------------- /diaspora_federation-rails.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("lib", __dir__) 4 | 5 | # Maintain your gem's version: 6 | require "diaspora_federation/version" 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = "diaspora_federation-rails" 11 | s.version = DiasporaFederation::VERSION 12 | s.authors = ["Benjamin Neff"] 13 | s.email = ["benjamin@coding4.coffee"] 14 | s.homepage = "https://github.com/diaspora/diaspora_federation" 15 | s.summary = "diaspora* federation rails engine" 16 | s.description = "A rails engine that adds the diaspora* federation protocol to a rails app" 17 | s.license = "AGPL-3.0" 18 | s.metadata = { 19 | "rubygems_mfa_required" => "true" 20 | } 21 | 22 | s.files = Dir["app/**/*", "config/routes.rb", "config/initializers/*", 23 | "lib/diaspora_federation/{engine,rails}.rb", "LICENSE", "README.md", "Changelog.md"] 24 | 25 | s.required_ruby_version = ">= 2.7" 26 | 27 | s.add_dependency "actionpack", ">= 5.2", "< 8" 28 | 29 | s.add_dependency "diaspora_federation", DiasporaFederation::VERSION 30 | end 31 | -------------------------------------------------------------------------------- /diaspora_federation-test.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("lib", __dir__) 4 | 5 | # Maintain your gem's version: 6 | require "diaspora_federation/version" 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = "diaspora_federation-test" 11 | s.version = DiasporaFederation::VERSION 12 | s.authors = ["Benjamin Neff"] 13 | s.email = ["benjamin@coding4.coffee"] 14 | s.homepage = "https://github.com/diaspora/diaspora_federation" 15 | s.summary = "diaspora* federation test utils" 16 | s.description = "This gem provides some supplimentary code (factory definitions), that" \ 17 | "helps to build tests for users of the diaspora_federation gem." 18 | s.license = "AGPL-3.0" 19 | s.metadata = { 20 | "rubygems_mfa_required" => "true" 21 | } 22 | 23 | s.files = Dir["lib/diaspora_federation/test.rb", "lib/diaspora_federation/test/*"] 24 | 25 | s.required_ruby_version = ">= 2.7" 26 | 27 | s.add_dependency "diaspora_federation", DiasporaFederation::VERSION 28 | s.add_dependency "fabrication", "~> 2.29" 29 | s.add_dependency "uuid", "~> 2.3", ">= 2.3.8" 30 | end 31 | -------------------------------------------------------------------------------- /diaspora_federation.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("lib", __dir__) 4 | 5 | # Maintain your gem's version: 6 | require "diaspora_federation/version" 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = "diaspora_federation" 11 | s.version = DiasporaFederation::VERSION 12 | s.authors = ["Benjamin Neff"] 13 | s.email = ["benjamin@coding4.coffee"] 14 | s.homepage = "https://github.com/diaspora/diaspora_federation" 15 | s.summary = "diaspora* federation library" 16 | s.description = "This gem provides the functionality for de-/serialization and " \ 17 | "de-/encryption of Entities in the protocols used for communication " \ 18 | "among the various installations of Diaspora*" 19 | s.license = "AGPL-3.0" 20 | s.metadata = { 21 | "rubygems_mfa_required" => "true" 22 | } 23 | 24 | s.files = Dir["lib/**/*", "LICENSE", "README.md", "Changelog.md"] - 25 | Dir["lib/diaspora_federation/{engine,rails,schemas,test}.rb", 26 | "lib/diaspora_federation/schemas/*", 27 | "lib/diaspora_federation/test/*", 28 | "lib/tasks/*.rake"] 29 | 30 | s.required_ruby_version = ">= 2.7" 31 | 32 | s.add_dependency "faraday", ">= 1.0", "< 3" 33 | s.add_dependency "faraday-follow_redirects", "~> 0.3" 34 | s.add_dependency "nokogiri", "~> 1.6", ">= 1.6.8" 35 | s.add_dependency "typhoeus", "~> 1.0" 36 | s.add_dependency "valid", "~> 1.0" 37 | end 38 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | url: "https://diaspora.github.io" # the base hostname & protocol 2 | baseurl: "/diaspora_federation" # the subpath 3 | contribute_url: "https://github.com/diaspora/diaspora_federation/blob/develop/CONTRIBUTING.md" 4 | 5 | sass: 6 | sass_dir: assets/css 7 | style: nested 8 | deploy_style: nested 9 | 10 | collections: 11 | entities: 12 | output: true 13 | 14 | kramdown: 15 | toc_levels: 2..2 16 | 17 | defaults: 18 | - 19 | scope: 20 | path: "" 21 | values: 22 | layout: "default" 23 | title: "" 24 | -------------------------------------------------------------------------------- /docs/_entities/account_deletion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AccountDeletion 3 | --- 4 | 5 | This entity is sent when a person closed the account. 6 | 7 | ## Properties 8 | 9 | | Property | Type | Description | 10 | | -------- | ---------------------------- | ---------------------------------------- | 11 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the closed account. | 12 | 13 | ## Example 14 | 15 | ~~~xml 16 | 17 | alice@example.org 18 | 19 | ~~~ 20 | 21 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 22 | -------------------------------------------------------------------------------- /docs/_entities/contact.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contact 3 | --- 4 | 5 | This entity represents a contact state with another person. 6 | 7 | When `blocking` is `true`, `following` and `sharing` need to be `false` (and the other way around). 8 | 9 | ## Properties 10 | 11 | | Property | Type | Description | 12 | | ----------- | ---------------------------- | --------------------------------------------------- | 13 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the sender of the contact. | 14 | | `recipient` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the recipient. | 15 | | `following` | [Boolean][boolean] | `true` if the author is following the recipient. | 16 | | `sharing` | [Boolean][boolean] | `true` if the author is sharing with the recipient. | 17 | 18 | ## Optional Properties 19 | 20 | | Property | Type | Description | 21 | | ---------- | ------------------ | ----------------------------------------------- | 22 | | `blocking` | [Boolean][boolean] | `true` if the author is blocking the recipient. | 23 | 24 | ## Example 25 | 26 | ~~~xml 27 | 28 | alice@example.org 29 | bob@example.com 30 | true 31 | true 32 | false 33 | 34 | ~~~ 35 | 36 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 37 | [boolean]: {{ site.baseurl }}/federation/types.html#boolean 38 | -------------------------------------------------------------------------------- /docs/_entities/location.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Location 3 | --- 4 | 5 | This entity represents the location data, it is nested in a [StatusMessage][status_message]. 6 | 7 | ## Properties 8 | 9 | | Property | Type (Length) | Description | 10 | | --------- | ---------------------- | ------------------------------------------------------------------------ | 11 | | `address` | [String][string] (255) | A string describing your location, e.g. a city name, a street name, etc. | 12 | | `lat` | [Float][float] | The geographical latitude of your location. | 13 | | `lng` | [Float][float] | The geographical longitude of your location. | 14 | 15 | ## Example 16 | 17 | ~~~xml 18 | 19 |
Vienna, Austria
20 | 48.208174 21 | 16.373819 22 |
23 | ~~~ 24 | 25 | [string]: {{ site.baseurl }}/federation/types.html#string 26 | [float]: {{ site.baseurl }}/federation/types.html#float 27 | [status_message]: {{ site.baseurl }}/entities/status_message.html 28 | -------------------------------------------------------------------------------- /docs/_entities/message.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Message 3 | --- 4 | 5 | This entity represents a private message exchanged in private conversation. It can be nested in a 6 | [Conversation][conversation] if it is the first message. 7 | 8 | ## Properties 9 | 10 | | Property | Type (Length) | Editable | Description | 11 | | ------------------- | ---------------------------- |:--------:| ----------------------------------------------- | 12 | | `author` | [diaspora\* ID][diaspora-id] | ✘ | The diaspora\* ID of the author of the message. | 13 | | `guid` | [GUID][guid] | ✘ | The GUID of the message. | 14 | | `conversation_guid` | [GUID][guid] | ✘ | The GUID of the [Conversation][conversation]. | 15 | | `text` | [Markdown][markdown] (65535) | ✔ | The message text. | 16 | | `created_at` | [Timestamp][timestamp] | ✘ | The create timestamp of the message. | 17 | 18 | ## Optional Properties 19 | 20 | | Property | Type (Length) | Editable | Description | 21 | | -------------------- | ---------------------- |:--------:| ------------------------------------------ | 22 | | `edited_at` | [Timestamp][timestamp] | ✔ | The timestamp when the message was edited. | 23 | 24 | ## Example 25 | 26 | ~~~xml 27 | 28 | alice@example.org 29 | 5cc5692029eb013487753131731751e9 30 | 9b1376a029eb013487753131731751e9 31 | this is a very informative text 32 | 2016-07-11T23:17:48Z 33 | 34 | ~~~ 35 | 36 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 37 | [guid]: {{ site.baseurl }}/federation/types.html#guid 38 | [markdown]: {{ site.baseurl }}/federation/types.html#markdown 39 | [timestamp]: {{ site.baseurl }}/federation/types.html#timestamp 40 | [conversation]: {{ site.baseurl }}/entities/conversation.html 41 | -------------------------------------------------------------------------------- /docs/_entities/participation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Participation 3 | --- 4 | 5 | A participation is sent to subscribe a person on updates for some [Post][post]. 6 | 7 | The `parent_type` can only be a [Post][post] (if it's a [StatusMessage][status_message] or [Reshare][reshare]) 8 | 9 | ## Properties 10 | 11 | | Property | Type | Description | 12 | | ------------- | ---------------------------- | ----------------------------------------------------- | 13 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the author of the participation. | 14 | | `guid` | [GUID][guid] | The GUID of the participation. | 15 | | `parent_guid` | [GUID][guid] | The GUID of the parent entity. | 16 | | `parent_type` | [Type][type] | The entity type of the parent. | 17 | 18 | ## Example 19 | 20 | ~~~xml 21 | 22 | alice@example.org 23 | 0840a9b029f6013487753131731751e9 24 | Post 25 | c3893bf029e7013487753131731751e9 26 | 27 | ~~~ 28 | 29 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 30 | [guid]: {{ site.baseurl }}/federation/types.html#guid 31 | [type]: {{ site.baseurl }}/federation/types.html#type 32 | [post]: {{ site.baseurl }}/entities/post.html 33 | [status_message]: {{ site.baseurl }}/entities/status_message.html 34 | [reshare]: {{ site.baseurl }}/entities/reshare.html 35 | -------------------------------------------------------------------------------- /docs/_entities/poll.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Poll 3 | --- 4 | 5 | This entity represents a poll, it is nested in a [StatusMessage][status_message]. 6 | 7 | ## Properties 8 | 9 | | Property | Type (Length) | Description | 10 | | ------------- | -------------------------- | ------------------------------ | 11 | | `guid` | [GUID][guid] | The GUID of the poll. | 12 | | `question` | [String][string] (255) | The question of the poll. | 13 | | `poll_answer` | [PollAnswer][poll_answer]s | At least 2 nested PollAnswers. | 14 | 15 | ## Example 16 | 17 | ~~~xml 18 | 19 | 2a22d6c029e9013487753131731751e9 20 | Select an answer 21 | 22 | 2a22db2029e9013487753131731751e9 23 | Yes 24 | 25 | 26 | 2a22e5e029e9013487753131731751e9 27 | No 28 | 29 | 30 | 2a22eca029e9013487753131731751e9 31 | Maybe 32 | 33 | 34 | ~~~ 35 | 36 | [guid]: {{ site.baseurl }}/federation/types.html#guid 37 | [string]: {{ site.baseurl }}/federation/types.html#string 38 | [poll_answer]: {{ site.baseurl }}/entities/poll_answer.html 39 | [status_message]: {{ site.baseurl }}/entities/status_message.html 40 | -------------------------------------------------------------------------------- /docs/_entities/poll_answer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PollAnswer 3 | --- 4 | 5 | This entity represents a answer of a [Poll][poll]. 6 | 7 | ## Properties 8 | 9 | | Property | Type (Length) | Description | 10 | | -------- | ---------------------- | ----------------------- | 11 | | `guid` | [GUID][guid] | The GUID of the answer. | 12 | | `answer` | [String][string] (255) | The answer text. | 13 | 14 | ## Example 15 | 16 | ~~~xml 17 | 18 | 2a22db2029e9013487753131731751e9 19 | Yes 20 | 21 | ~~~ 22 | 23 | [guid]: {{ site.baseurl }}/federation/types.html#guid 24 | [string]: {{ site.baseurl }}/federation/types.html#string 25 | [poll]: {{ site.baseurl }}/entities/poll.html 26 | -------------------------------------------------------------------------------- /docs/_entities/poll_participation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PollParticipation 3 | --- 4 | 5 | This entity represents a participation in a [Poll][poll]. 6 | 7 | See also: [Relayable][relayable] 8 | 9 | ## Properties 10 | 11 | | Property | Type | Description | 12 | | ------------------------- | ---------------------------- | ---------------------------------------------------------- | 13 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the author of the poll participation. | 14 | | `guid` | [GUID][guid] | The GUID of the poll participation. | 15 | | `parent_guid` | [GUID][guid] | The GUID of the [Poll][poll]. | 16 | | `poll_answer_guid` | [GUID][guid] | The GUID of the [PollAnswer][poll_answer]. | 17 | | `author_signature` | [Signature][signature] | The signature from the author of the poll participation. | 18 | 19 | ## Examples 20 | 21 | ~~~xml 22 | 23 | f1eb866029f7013487753131731751e9 24 | 2a22d6c029e9013487753131731751e9 25 | alice@example.org 26 | 2a22db2029e9013487753131731751e9 27 | dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY= 28 | 29 | ~~~ 30 | 31 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 32 | [guid]: {{ site.baseurl }}/federation/types.html#guid 33 | [signature]: {{ site.baseurl }}/federation/types.html#signature 34 | [poll]: {{ site.baseurl }}/entities/poll.html 35 | [poll_answer]: {{ site.baseurl }}/entities/poll_answer.html 36 | [relayable]: {{ site.baseurl }}/federation/relayable.html 37 | -------------------------------------------------------------------------------- /docs/_entities/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post 3 | --- 4 | 5 | This is the abstract parent type of [StatusMessage][status_message] and currently still [Reshare][reshare]. 6 | 7 | {% include warning_box.html 8 | title="Future of reshares" 9 | content="

Reshare will not inherit from Post in the future anymore! More information about this 10 | can be found here or 11 | in this issue.

" 12 | %} 13 | 14 | ## Common Properties 15 | 16 | | Property | Type (Length) | Description | 17 | | ------------ | ---------------------------- | -------------------------------------------- | 18 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the author of the post. | 19 | | `guid` | [GUID][guid] | The GUID of the post. | 20 | | `created_at` | [Timestamp][timestamp] | The create timestamp of the post. | 21 | | `public` | [Boolean][boolean] | `true` if the post is public. | 22 | 23 | ## Common Optional Properties 24 | 25 | | Property | Type (Length) | Description | 26 | | ----------------------- | ---------------------- | -------------------------------------------------- | 27 | | `provider_display_name` | [String][string] (255) | The means by which the author has posted the post. | 28 | 29 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 30 | [guid]: {{ site.baseurl }}/federation/types.html#guid 31 | [timestamp]: {{ site.baseurl }}/federation/types.html#timestamp 32 | [boolean]: {{ site.baseurl }}/federation/types.html#boolean 33 | [string]: {{ site.baseurl }}/federation/types.html#string 34 | [status_message]: {{ site.baseurl }}/entities/status_message.html 35 | [reshare]: {{ site.baseurl }}/entities/reshare.html 36 | -------------------------------------------------------------------------------- /docs/_entities/retraction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Retraction 3 | --- 4 | 5 | This entity represents a claim of deletion of a previously federated entity. 6 | 7 | ## Properties 8 | 9 | | Property | Type | Description | 10 | | ------------- | ---------------------------- | ------------------------------------------------- | 11 | | `author` | [diaspora\* ID][diaspora-id] | The diaspora\* ID of the who claims the deletion. | 12 | | `target_guid` | [GUID][guid] | The GUID of the entity to delete. | 13 | | `target_type` | [Type][type] | The type of the entity to delete. | 14 | 15 | ## Example 16 | 17 | ~~~xml 18 | 19 | alice@example.org 20 | 8d89e1f029f6013487753131731751e9 21 | Post 22 | 23 | ~~~ 24 | 25 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 26 | [guid]: {{ site.baseurl }}/federation/types.html#guid 27 | [type]: {{ site.baseurl }}/federation/types.html#type 28 | -------------------------------------------------------------------------------- /docs/_includes/active_class.html: -------------------------------------------------------------------------------- 1 | {% if page.url == include.url %}active{% endif %} 2 | -------------------------------------------------------------------------------- /docs/_includes/discovery_tree.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /docs/_includes/entity_tree.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /docs/_includes/federation_tree.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /docs/_includes/top_navbar.html: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /docs/_includes/warning_box.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ include.title }}

3 |
{{ include.content }}
4 |
5 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% capture page_title %} 10 | {% if page.title != "" %} 11 | {{ page.title }} - diaspora* federation protocol 12 | {% else %} 13 | diaspora* federation protocol 14 | {% endif %} 15 | {% endcapture %} 16 | {{ page_title | strip }} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | {% include top_navbar.html %} 27 | 28 |
29 | 45 |
46 | {% if page.title != "" %} 47 |

{{ page.title }}

48 | {% endif %} 49 | 50 | {{ content }} 51 |
52 |
53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/assets/css/_theme.scss: -------------------------------------------------------------------------------- 1 | body { 2 | color: #bfbfbf; 3 | background-color: #181818; 4 | } 5 | 6 | .navbar-default { 7 | background-color: #0f0f0f; 8 | border-color: #222; 9 | 10 | .navbar-nav > { 11 | .active, 12 | .open { 13 | > a { 14 | &, 15 | &:focus, 16 | &:hover { 17 | color: #fff; 18 | background-color: #555; 19 | } 20 | } 21 | 22 | .dropdown-menu > { 23 | .active > a { 24 | &, 25 | &:focus, 26 | &:hover { 27 | color: #fff; 28 | background-color: #555; 29 | } 30 | } 31 | 32 | li > a { 33 | &:focus, 34 | &:hover { 35 | color: #fff; 36 | } 37 | } 38 | } 39 | } 40 | 41 | li > a { 42 | &:focus, 43 | &:hover { 44 | color: #fff; 45 | } 46 | } 47 | } 48 | } 49 | 50 | .nav > li > a { 51 | &:focus, 52 | &:hover { 53 | color: #fff; 54 | background-color: #555; 55 | } 56 | } 57 | 58 | code, pre { 59 | color: #bfbfbf; 60 | background-color: #181818; 61 | border: 1px solid #474747; 62 | } 63 | pre code { 64 | border: 0; 65 | } 66 | 67 | .panel { 68 | background-color: #181818; 69 | } 70 | .panel-warning { 71 | border-color: #645a1b; 72 | 73 | > .panel-heading { 74 | color: #cebc4a; 75 | background-color: #645a1b; 76 | border-color: #141205; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /docs/assets/css/main.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "pygments"; 5 | @import "webfonts"; 6 | @import "theme"; 7 | 8 | .container-fluid { 9 | max-width: 1200px; 10 | } 11 | 12 | body { 13 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | padding-bottom: 70px; 15 | padding-top: 20px; 16 | } 17 | 18 | .navbar { 19 | margin-bottom: 20px; 20 | } 21 | 22 | .sidebar { 23 | .nav-header { 24 | margin-left: 15px; 25 | margin-top: 25px; 26 | } 27 | } 28 | 29 | .docbody { 30 | h1:first-child { 31 | margin-top: 0; 32 | } 33 | 34 | h2 { 35 | border-top: 1px solid #222; 36 | margin-top: 30px; 37 | padding-top: 25px; 38 | } 39 | 40 | pre { 41 | max-height: 300px; 42 | 43 | code { 44 | font-family: "Fira Mono", monospace, monospace; 45 | overflow: auto; 46 | white-space: pre; 47 | word-wrap: normal; 48 | } 49 | } 50 | 51 | .panel-body p:last-child { 52 | margin-bottom: 0; 53 | } 54 | 55 | /** 56 | * Based on the Twitter Bootstrap .table styles. See 57 | * https://github.com/twbs/bootstrap/blob/master/less/tables.less 58 | */ 59 | table { 60 | margin-bottom: 20px; 61 | max-width: 100%; 62 | width: 100%; 63 | 64 | thead tr th { 65 | border-bottom: 2px solid #474747; 66 | line-height: 1.42857143; 67 | padding: 8px; 68 | vertical-align: bottom; 69 | } 70 | 71 | tbody tr { 72 | td { 73 | border-top: 1px solid #474747; 74 | line-height: 1.42857143; 75 | padding: 8px; 76 | vertical-align: top; 77 | } 78 | 79 | &:nth-of-type(2n+1) { 80 | background-color: #222; 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Bold.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Bold.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Bold.woff -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Regular.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraMono-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraMono-Regular.woff -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Bold.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Bold.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Bold.woff -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-BoldItalic.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-BoldItalic.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-BoldItalic.woff -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Italic.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Italic.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Italic.woff -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Regular.eot -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/fonts/FiraSans-Regular.woff -------------------------------------------------------------------------------- /docs/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/images/favicon.ico -------------------------------------------------------------------------------- /docs/assets/vendor/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/vendor/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/assets/vendor/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/vendor/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/assets/vendor/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/vendor/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/assets/vendor/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/docs/assets/vendor/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/entities/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Entities 3 | --- 4 | 5 | The diaspora\* federation protocol currently knows the following entities: 6 | 7 | {% for entity in site.entities %} 8 | * [{{ entity.title }}]({{ site.baseurl }}{{ entity.url }}) 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /docs/federation/diaspora_scheme.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: diaspora:// URI scheme 3 | --- 4 | 5 | ## Server and software independent links 6 | 7 | A `diaspora://` URL is used if a user wants to link to another post. It doesn't 8 | contain a server hostname so it is independent of the senders server. And it 9 | isn't software specific, it is thought to be compatible with every software 10 | that is compatible with the protocol, so the receiving software can display 11 | it as software specific URL. 12 | 13 | The format is similar to the route used for [fetching][fetching], so if the 14 | receiving server doesn't know the linked entity yet, it can just be fetched. 15 | 16 | When the entity with that `guid` is already available locally, the recipient 17 | should validate that it's from `author` before linking to it. 18 | 19 | ### Format 20 | 21 | `diaspora://:author/:type/:guid` 22 | 23 | #### Parameters 24 | 25 | | Name | Description | 26 | | -------- | -------------------------------------------------------------------- | 27 | | `author` | The [diaspora\* ID][diaspora-id] of the author of the linked entity. | 28 | | `type` | The type of the linked entity in `snake_case`. | 29 | | `guid` | The [GUID][guid] of the linked entity. | 30 | 31 | #### Example 32 | 33 | `diaspora://alice@example.org/post/17faf230675101350d995254001bd39e` 34 | 35 | [fetching]: {{ site.baseurl }}/federation/fetching.html 36 | [diaspora-id]: {{ site.baseurl }}/federation/types.html#diaspora-id 37 | [guid]: {{ site.baseurl }}/federation/types.html#guid 38 | -------------------------------------------------------------------------------- /docs/federation/encryption.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Encryption 3 | --- 4 | 5 | diaspora\* wraps the Salmon [Magic Envelope][magicsig] into a simple JSON structure, to encrypt private messages. 6 | 7 | ## Encrypted Magic Envelope 8 | 9 | ### JSON structure 10 | 11 | ~~~json 12 | { 13 | "aes_key": "...", 14 | "encrypted_magic_envelope": "..." 15 | } 16 | ~~~ 17 | 18 | | Key | Description | 19 | | -------------------------- |------------------------------------------------------------------------------------------------------------------------ | 20 | | `aes_key` | The [AES Key JSON](#aes-key-json-structure) encrypted with the recipients public key using RSA and then base64 encoded. | 21 | | `encrypted_magic_envelope` | The [Magic Envelope][magicsig] encrypted with the `aes_key` using AES-256-CBC and then base64 encoded. | 22 | 23 | ### AES Key JSON structure 24 | 25 | ~~~json 26 | { 27 | "key": "...", 28 | "iv": "..." 29 | } 30 | ~~~ 31 | 32 | | Key | Description | 33 | | ----- |---------------------------- | 34 | | `key` | The base64 encoded AES key. | 35 | | `iv` | The base64 encoded AES iv. | 36 | 37 | Both `key` and `iv` have to be suitable for AES-256-CBC. 38 | 39 | ## Additional information and specifications 40 | 41 | * [Magic Envelope][magicsig] 42 | 43 | [magicsig]: {{ site.baseurl }}/federation/magicsig.html#magic-envelope 44 | -------------------------------------------------------------------------------- /docs/federation/xml_serialization.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: XML Serialization 3 | --- 4 | 5 | The [entities][entities] are serialized to XML, before they are added to the [Magic Envelope][magicsig]. 6 | 7 | ## Root element 8 | 9 | The root element is named like the entity-name in `snake_case`. 10 | 11 | Examples: `status_message`, `comment`, `like` 12 | 13 | ## Properties 14 | 15 | The properties are added as child-elements, with the value string-serialized as described in [value formats][types] as 16 | content of the element. 17 | 18 | The order of the elements is not specified. 19 | 20 | Elements may be empty, if they have no value. 21 | 22 | Unknown elements must be ignored while parsing (except for signature-verification of [relayables][relayables]). 23 | 24 | ## Nested Entities 25 | 26 | Some entities have other entities as properties. The XML of the nested entity is nested into the root element of the 27 | parent entity. The same entity-type can be nested multiple times (e.g. `poll_answer` in `poll`). 28 | 29 | ## Example 30 | 31 | ~~~xml 32 | 33 | alice@example.org 34 | 17418fb029e6013487743131731751e9 35 | 2016-07-11T22:38:19Z 36 | 37 | I am a very interesting status update 38 | true 39 | 40 | ~~~ 41 | 42 | [entities]: {{ site.baseurl }}/entities/ 43 | [magicsig]: {{ site.baseurl }}/federation/magicsig.html 44 | [types]: {{ site.baseurl }}/federation/types.html 45 | [relayables]: {{ site.baseurl }}/federation/relayable.html 46 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | # diaspora\* federation protocol 5 | 6 | The purpose of this document is to specify the communications that go on between diaspora\* servers (and other servers 7 | supporting this protocol). If you experience any issues, feel free to [get in touch][communication] with us. 8 | 9 | ## Federation support 10 | 11 | This document specifies the future protocol for diaspora. diaspora\* release 0.6.3.0 and newer has support to receive 12 | [entities][entities] with this protocol, but still sends entities with an older protocol. Starting with diaspora\* 13 | release 0.7.0.0 this protocol is fully supported. 14 | 15 | ## Implementations 16 | 17 | An implementation of this protocol is available as a Ruby Gem under the AGPL [on Github][github]. This is the library used by the diaspora* project. 18 | 19 | The [Friendica][friendica] project also has [its implementation in PHP][phpimplementation]. 20 | 21 | [communication]: https://wiki.diasporafoundation.org/How_we_communicate 22 | [entities]: {{ site.baseurl }}/entities/ 23 | [github]: https://github.com/diaspora/diaspora_federation 24 | [friendica]: https://github.com/friendica/friendica 25 | [phpimplementation]: https://github.com/friendica/friendica/blob/develop/src/Protocol/Diaspora.php 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/callbacks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # Callbacks are used to communicate with the application. They are called to 5 | # fetch data and after data is received. 6 | class Callbacks 7 | # Initializes a new Callbacks object with the event-keys that need to be defined 8 | # 9 | # @example 10 | # Callbacks.new %i( 11 | # some_event 12 | # another_event 13 | # ) 14 | # 15 | # @param [Hash] events event keys 16 | def initialize(events) 17 | @events = events 18 | @handlers = {} 19 | end 20 | 21 | # Defines a callback 22 | # 23 | # @example 24 | # callbacks.on :some_event do |arg1| 25 | # # do something 26 | # end 27 | # 28 | # @param [Symbol] event the event key 29 | # @param [Proc] callback the callback block 30 | # @raise [ArgumentError] if the event key is undefined or has already a handler 31 | def on(event, &callback) 32 | raise ArgumentError, "Undefined event #{event}" unless @events.include? event 33 | raise ArgumentError, "Already defined event #{event}" if @handlers.has_key? event 34 | 35 | @handlers[event] = callback 36 | end 37 | 38 | # Triggers a callback 39 | # 40 | # @example 41 | # callbacks.trigger :some_event, "foo" 42 | # 43 | # @param [Symbol] event the event key 44 | # @return [Object] the return-value of the callback 45 | # @raise [ArgumentError] if the event key is undefined 46 | def trigger(event, *args) 47 | raise ArgumentError, "Undefined event #{event}" unless @events.include? event 48 | 49 | @handlers[event].call(*args) 50 | end 51 | 52 | # Checks if all callbacks are defined 53 | # @return [Boolean] 54 | def definition_complete? 55 | missing_handlers.empty? 56 | end 57 | 58 | # Returns all undefined callbacks 59 | # @return [Hash] callback keys 60 | def missing_handlers 61 | @events - @handlers.keys 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/diaspora_federation/discovery.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # This module provides the namespace for the various classes implementing 5 | # WebFinger and other protocols used for metadata discovery on remote servers 6 | # in the diaspora* network. 7 | module Discovery 8 | end 9 | end 10 | 11 | require "diaspora_federation/discovery/exceptions" 12 | require "diaspora_federation/discovery/xrd_document" 13 | require "diaspora_federation/discovery/web_finger" 14 | require "diaspora_federation/discovery/h_card" 15 | require "diaspora_federation/discovery/discovery" 16 | -------------------------------------------------------------------------------- /lib/diaspora_federation/discovery/exceptions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Discovery 5 | # Raised, if there is an error while discover a new person 6 | class DiscoveryError < RuntimeError 7 | end 8 | 9 | # Raised, if the XML structure is invalid 10 | class InvalidDocument < DiscoveryError 11 | end 12 | 13 | # Raised, if something is wrong with the webfinger data 14 | # 15 | # * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml} 16 | # * if the parsed XML from {WebFinger.from_xml} is incomplete 17 | # * if the html passed to {HCard.from_html} in some way is malformed, invalid or incomplete. 18 | class InvalidData < DiscoveryError 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/diaspora_federation/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # diaspora* federation rails engine 5 | class Engine < ::Rails::Engine 6 | isolate_namespace DiasporaFederation 7 | 8 | config.generators do |g| 9 | g.test_framework :rspec 10 | end 11 | 12 | config.after_initialize do 13 | DiasporaFederation.validate_config 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # This namespace contains all the entities used to encapsulate data that is 5 | # passed around in the diaspora* network as part of the federation protocol. 6 | # 7 | # All entities must be defined in this namespace. Otherwise the XML 8 | # de-serialization will fail. 9 | module Entities 10 | end 11 | end 12 | 13 | require "diaspora_federation/entities/related_entity" 14 | 15 | # abstract types 16 | require "diaspora_federation/entities/post" 17 | require "diaspora_federation/entities/signable" 18 | require "diaspora_federation/entities/account_migration/signable" 19 | require "diaspora_federation/entities/relayable" 20 | 21 | # types 22 | require "diaspora_federation/entities/profile" 23 | require "diaspora_federation/entities/person" 24 | require "diaspora_federation/entities/contact" 25 | require "diaspora_federation/entities/account_deletion" 26 | require "diaspora_federation/entities/account_migration" 27 | 28 | require "diaspora_federation/entities/participation" 29 | require "diaspora_federation/entities/like" 30 | require "diaspora_federation/entities/comment" 31 | require "diaspora_federation/entities/poll_answer" 32 | require "diaspora_federation/entities/poll" 33 | require "diaspora_federation/entities/poll_participation" 34 | 35 | require "diaspora_federation/entities/embed" 36 | require "diaspora_federation/entities/location" 37 | 38 | require "diaspora_federation/entities/event" 39 | require "diaspora_federation/entities/event_participation" 40 | 41 | require "diaspora_federation/entities/photo" 42 | require "diaspora_federation/entities/status_message" 43 | require "diaspora_federation/entities/reshare" 44 | 45 | require "diaspora_federation/entities/message" 46 | require "diaspora_federation/entities/conversation" 47 | 48 | require "diaspora_federation/entities/retraction" 49 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/account_deletion.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity is sent when an account was deleted on a remote pod. 6 | # 7 | # @see Validators::AccountDeletionValidator 8 | class AccountDeletion < Entity 9 | # @!attribute [r] author 10 | # The diaspora* ID of the deleted account 11 | # @see Person#author 12 | # @return [String] diaspora* ID 13 | # @!attribute [r] diaspora_id 14 | # Alias for author 15 | # @see AccountDeletion#author 16 | # @return [String] diaspora* ID 17 | property :author, :string, alias: :diaspora_id 18 | 19 | # @return [String] string representation of this object 20 | def to_s 21 | "AccountDeletion:#{author}" 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/account_migration/signable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | class AccountMigration < Entity 6 | # AccountMigration::Signable is a module that encapsulates basic signature generation/verification flow for 7 | # AccountMigration entity. 8 | # 9 | # It is possible that implementation of diaspora* protocol requires to compute the signature for the 10 | # AccountMigration entity without instantiating the entity. In this case this module may be useful. 11 | module Signable 12 | include Entities::Signable 13 | 14 | # @return [String] string which is uniquely represents migration occasion 15 | def unique_migration_descriptor 16 | "AccountMigration:#{old_identity}:#{new_identity}" 17 | end 18 | 19 | # @see DiasporaFederation::Entities::Signable#signature_data 20 | def signature_data 21 | unique_migration_descriptor 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/comment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a comment to some kind of post (e.g. status message). 6 | # 7 | # @see Validators::CommentValidator 8 | class Comment < Entity 9 | # The {Comment} parent is a {Post} 10 | PARENT_TYPE = "Post" 11 | 12 | include Relayable 13 | 14 | # @!attribute [r] text 15 | # @return [String] the comment text 16 | property :text, :string 17 | 18 | # @!attribute [r] created_at 19 | # Comment entity creation time 20 | # @return [Time] creation time 21 | property :created_at, :timestamp, default: -> { Time.now.utc } 22 | 23 | # @!attribute [r] edited_at 24 | # The timestamp when the comment was edited 25 | # @return [Time] edited time 26 | property :edited_at, :timestamp, optional: true 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/contact.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a contact with another person. A user issues it 6 | # when they start sharing/following with another user. 7 | # 8 | # @see Validators::ContactValidator 9 | class Contact < Entity 10 | # @!attribute [r] author 11 | # The diaspora* ID of the person who shares their profile 12 | # @see Person#author 13 | # @return [String] sender ID 14 | property :author, :string 15 | 16 | # @!attribute [r] recipient 17 | # The diaspora* ID of the person who will be shared with 18 | # @see Validation::Rule::DiasporaId 19 | # @return [String] recipient ID 20 | property :recipient, :string 21 | 22 | # @!attribute [r] following 23 | # @return [Boolean] if the author is following the person 24 | property :following, :boolean, default: true 25 | 26 | # @!attribute [r] sharing 27 | # @return [Boolean] if the author is sharing with the person 28 | property :sharing, :boolean, default: true 29 | 30 | # @!attribute [r] blocking 31 | # @return [Boolean] if the author is blocking the person 32 | property :blocking, :boolean, optional: true, default: false 33 | 34 | # @return [String] string representation of this object 35 | def to_s 36 | "Contact:#{author}:#{recipient}" 37 | end 38 | 39 | private 40 | 41 | def validate 42 | super 43 | 44 | return unless (following || sharing) && blocking 45 | 46 | raise ValidationError, 47 | "flags invalid: following:#{following}/sharing:#{sharing} and blocking:#{blocking} can't both be true" 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/conversation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a private conversation between users. 6 | # 7 | # @see Validators::ConversationValidator 8 | class Conversation < Entity 9 | # @!attribute [r] author 10 | # The diaspora* ID of the person initiated the conversation 11 | # @see Person#author 12 | # @return [String] diaspora* ID 13 | property :author, :string 14 | 15 | # @!attribute [r] guid 16 | # A random string of at least 16 chars 17 | # @see Validation::Rule::Guid 18 | # @return [String] conversation guid 19 | property :guid, :string 20 | 21 | # @!attribute [r] subject 22 | # @return [String] the conversation subject 23 | property :subject, :string 24 | 25 | # @!attribute [r] created_at 26 | # @return [Time] Conversation creation time 27 | property :created_at, :timestamp, default: -> { Time.now.utc } 28 | 29 | # @!attribute [r] participants 30 | # The diaspora* IDs of the persons participating the conversation separated by ";" 31 | # @return [String] participants diaspora* IDs 32 | property :participants, :string 33 | 34 | # @!attribute [r] messages 35 | # @return [[Entities::Message]] Messages of this conversation 36 | entity :messages, [Entities::Message], default: [] 37 | 38 | private 39 | 40 | def validate 41 | super 42 | messages.each do |message| 43 | raise ValidationError, "nested #{message} has different author: author=#{author}" if message.author != author 44 | end 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/embed.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity is used to specify embed information about an URL that should be embedded. 6 | # 7 | # @see Validators::EmbedValidator 8 | class Embed < Entity 9 | # @!attribute [r] url 10 | # URL that should be embedded. 11 | # @return [String] url 12 | property :url, :string, optional: true 13 | 14 | # @!attribute [r] title 15 | # The title of the embedded URL. 16 | # @return [String] title 17 | property :title, :string, optional: true 18 | 19 | # @!attribute [r] description 20 | # The description of the embedded URL. 21 | # @return [String] description 22 | property :description, :string, optional: true 23 | 24 | # @!attribute [r] image 25 | # The image of the embedded URL. 26 | # @return [String] image 27 | property :image, :string, optional: true 28 | 29 | # @!attribute [r] nothing 30 | # True, if nothing should be embedded. 31 | # @return [String] nothing 32 | property :nothing, :boolean, optional: true 33 | 34 | # @return [String] string representation of this object 35 | def to_s 36 | "Embed#{":#{url}" if url}" 37 | end 38 | 39 | def validate 40 | super 41 | 42 | raise ValidationError, "Either 'url' must be set or 'nothing' must be 'true'" unless nothing ^ url 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/event_participation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a participation in an event, i.e. it is issued when a user responds to en event. 6 | # 7 | # @see Validators::EventParticipationValidator 8 | class EventParticipation < Entity 9 | # The {EventParticipation} parent is an {Event} 10 | PARENT_TYPE = "Event" 11 | 12 | include Relayable 13 | 14 | # @!attribute [r] status 15 | # The participation status of the user 16 | # "accepted", "declined" or "tentative" 17 | # @return [String] event participation status 18 | property :status, :string 19 | 20 | # @!attribute [r] edited_at 21 | # The timestamp when the event participation was edited 22 | # @return [Time] edited time 23 | property :edited_at, :timestamp, optional: true 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/like.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a like to some kind of post (e.g. status message). 6 | # 7 | # @see Validators::LikeValidator 8 | class Like < Entity 9 | include Relayable 10 | 11 | # @!attribute [r] parent_type 12 | # A string describing the type of the parent 13 | # Can be "Post" or "Comment" (Comments are currently not implemented in the 14 | # diaspora* frontend). 15 | # @return [String] parent type 16 | property :parent_type, :string 17 | 18 | # @!attribute [r] positive 19 | # If +true+ set a like, if +false+, set a dislike (dislikes are currently not 20 | # implemented in the diaspora* frontend). 21 | # @return [Boolean] is it a like or a dislike 22 | property :positive, :boolean 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/location.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity is used to specify location data and used embedded in a status message. 6 | # 7 | # @see Validators::LocationValidator 8 | class Location < Entity 9 | # @!attribute [r] address 10 | # A string describing your location, e.g. a city name, a street name, etc 11 | # @return [String] address 12 | property :address, :string 13 | 14 | # @!attribute [r] lat 15 | # Geographical latitude of your location 16 | # @return [String] latitude 17 | property :lat, :string 18 | 19 | # @!attribute [r] lng 20 | # Geographical longitude of your location 21 | # @return [String] longitude 22 | property :lng, :string 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/message.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a private message exchanged in private conversation. 6 | # 7 | # @see Validators::MessageValidator 8 | class Message < Entity 9 | # @!attribute [r] author 10 | # The diaspora* ID of the author 11 | # @see Person#author 12 | # @return [String] diaspora* ID 13 | property :author, :string 14 | 15 | # @!attribute [r] guid 16 | # A random string of at least 16 chars 17 | # @see Validation::Rule::Guid 18 | # @return [String] guid 19 | property :guid, :string 20 | 21 | # @!attribute [r] text 22 | # Text of the message composed by a user 23 | # @return [String] text 24 | property :text, :string 25 | 26 | # @!attribute [r] created_at 27 | # Message creation time 28 | # @return [Time] creation time 29 | property :created_at, :timestamp, default: -> { Time.now.utc } 30 | 31 | # @!attribute [r] edited_at 32 | # The timestamp when the message was edited 33 | # @return [Time] edited time 34 | property :edited_at, :timestamp, optional: true 35 | 36 | # @!attribute [r] conversation_guid 37 | # Guid of a conversation this message belongs to 38 | # @see Conversation#guid 39 | # @return [String] conversation guid 40 | property :conversation_guid, :string 41 | 42 | # @return [String] string representation of this object 43 | def to_s 44 | "#{super}:#{conversation_guid}" 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/participation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # Participation is sent to subscribe a user on updates for some post. 6 | # 7 | # @see Validators::Participation 8 | class Participation < Entity 9 | # @!attribute [r] author 10 | # The diaspora* ID of the author 11 | # @see Person#author 12 | # @return [String] diaspora* ID 13 | property :author, :string 14 | 15 | # @!attribute [r] guid 16 | # A random string of at least 16 chars 17 | # @see Validation::Rule::Guid 18 | # @return [String] guid 19 | property :guid, :string 20 | 21 | # @!attribute [r] parent_guid 22 | # @see StatusMessage#guid 23 | # @return [String] parent guid 24 | property :parent_guid, :string 25 | 26 | # @!attribute [r] parent_type 27 | # A string describing a type of the target to subscribe on 28 | # Currently only "Post" is supported. 29 | # @return [String] parent type 30 | property :parent_type, :string 31 | 32 | # @return [String] string representation of this object 33 | def to_s 34 | "#{super}:#{parent_type}:#{parent_guid}" 35 | end 36 | 37 | # Validates that the parent exists and the parent author is local 38 | def validate_parent 39 | parent = DiasporaFederation.callbacks.trigger(:fetch_related_entity, parent_type, parent_guid) 40 | raise ParentNotLocal, "obj=#{self}" unless parent&.local 41 | end 42 | 43 | # Validate that the parent is local. 44 | # @see Entity.from_hash 45 | # @param [Hash] hash entity initialization hash 46 | # @return [Entity] instance 47 | def self.from_hash(hash) 48 | super.tap(&:validate_parent) 49 | end 50 | 51 | # Raised, if the parent is not owned by the receiving pod. 52 | class ParentNotLocal < RuntimeError 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/person.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity contains the base data of a person. 6 | # 7 | # @see Validators::PersonValidator 8 | class Person < Entity 9 | # @!attribute [r] guid 10 | # This is just the guid. When a user creates an account on a pod, the pod 11 | # MUST assign them a guid - a random string of at least 16 chars. 12 | # @see Validation::Rule::Guid 13 | # @return [String] guid 14 | property :guid, :string 15 | 16 | # @!attribute [r] author 17 | # The diaspora* ID of the person 18 | # @see Validation::Rule::DiasporaId 19 | # @return [String] diaspora* ID 20 | # @!attribute [r] diaspora_id 21 | # alias for author 22 | # @see Person#author 23 | # @return [String] diaspora* ID 24 | property :author, :string, alias: :diaspora_id 25 | 26 | # @!attribute [r] url 27 | # @see Discovery::WebFinger#seed_url 28 | # @return [String] link to the pod 29 | property :url, :string 30 | 31 | # @!attribute [r] profile 32 | # All profile data of the person 33 | # @return [Profile] the profile of the person 34 | entity :profile, Entities::Profile 35 | 36 | # @!attribute [r] exported_key 37 | # @see Discovery::HCard#public_key 38 | # @return [String] public key 39 | property :exported_key, :string 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/poll.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a poll and it is federated as an optional part of a status message. 6 | # 7 | # @see Validators::PollValidator 8 | class Poll < Entity 9 | # @!attribute [r] guid 10 | # A random string of at least 16 chars 11 | # @see Validation::Rule::Guid 12 | # @return [String] poll guid 13 | property :guid, :string 14 | 15 | # @!attribute [r] question 16 | # Text of the question posed by a user 17 | # @return [String] question 18 | property :question, :string 19 | 20 | # @!attribute [r] poll_answers 21 | # Array of possible answers for the poll 22 | # @return [[Entities::PollAnswer]] poll answers 23 | entity :poll_answers, [Entities::PollAnswer] 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/poll_answer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a poll answer and is federated as a part of the Poll entity. 6 | # 7 | # @see Validators::PollAnswerValidator 8 | class PollAnswer < Entity 9 | # @!attribute [r] guid 10 | # A random string of at least 16 chars 11 | # @see Validation::Rule::Guid 12 | # @return [String] guid 13 | property :guid, :string 14 | 15 | # @!attribute [r] answer 16 | # Text of the answer 17 | # @return [String] answer 18 | property :answer, :string 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/poll_participation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a participation in poll, i.e. it is issued when a user votes for an answer in a poll. 6 | # 7 | # @see Validators::PollParticipationValidator 8 | class PollParticipation < Entity 9 | # The {PollParticipation} parent is a {Poll} 10 | PARENT_TYPE = "Poll" 11 | 12 | include Relayable 13 | 14 | # @!attribute [r] poll_answer_guid 15 | # Guid of the answer selected by the user 16 | # @see PollAnswer#guid 17 | # @return [String] poll answer guid 18 | property :poll_answer_guid, :string 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/post.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This is a module that defines common properties for a post which 6 | # include {StatusMessage} and {Reshare}. 7 | module Post 8 | # On inclusion of this module the required properties for a post are added to the object that includes it. 9 | # 10 | # @!attribute [r] author 11 | # The diaspora* ID of the person who posts the post 12 | # @see Person#author 13 | # @return [String] diaspora* ID 14 | # 15 | # @!attribute [r] guid 16 | # A random string of at least 16 chars 17 | # @see Validation::Rule::Guid 18 | # @return [String] status message guid 19 | # 20 | # @!attribute [r] created_at 21 | # Post entity creation time 22 | # @return [Time] creation time 23 | # 24 | # @!attribute [r] public 25 | # Shows whether the post is visible to everyone or only to some aspects 26 | # @return [Boolean] is it public 27 | # 28 | # @!attribute [r] provider_display_name 29 | # A string that describes a means by which a user has posted the post 30 | # @return [String] provider display name 31 | # 32 | # @param [Entity] entity the entity in which it is included 33 | def self.included(entity) 34 | entity.class_eval do 35 | property :author, :string 36 | property :guid, :string 37 | property :created_at, :timestamp, default: -> { Time.now.utc } 38 | property :public, :boolean, default: false 39 | property :provider_display_name, :string, optional: true 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/diaspora_federation/entities/status_message.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Entities 5 | # This entity represents a status message sent by a user. 6 | # 7 | # @see Validators::StatusMessageValidator 8 | class StatusMessage < Entity 9 | include Post 10 | 11 | # @!attribute [r] text 12 | # Text of the status message composed by the user 13 | # @return [String] text of the status message 14 | property :text, :string 15 | 16 | # @!attribute [r] edited_at 17 | # The timestamp when the status message was edited 18 | # @return [Time] edited time 19 | property :edited_at, :timestamp, optional: true 20 | 21 | # @!attribute [r] photos 22 | # Optional photos attached to the status message 23 | # @return [[Entities::Photo]] photos 24 | entity :photos, [Entities::Photo], optional: true, default: [] 25 | 26 | # @!attribute [r] location 27 | # Optional location attached to the status message 28 | # @return [Entities::Location] location 29 | entity :location, Entities::Location, optional: true 30 | 31 | # @!attribute [r] poll 32 | # Optional poll attached to the status message 33 | # @return [Entities::Poll] poll 34 | entity :poll, Entities::Poll, optional: true 35 | 36 | # @!attribute [r] event 37 | # Optional event attached to the status message 38 | # @return [Entities::Event] event 39 | entity :event, Entities::Event, optional: true 40 | 41 | # @!attribute [r] embed 42 | # Optional embed information of an URL that should be embedded in the status message 43 | # @return [Entities::Embed] embed 44 | entity :embed, Entities::Embed, optional: true 45 | 46 | private 47 | 48 | def validate 49 | super 50 | photos.each do |photo| 51 | if photo.author != author 52 | raise ValidationError, "nested #{photo} has different author: author=#{author} obj=#{self}" 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # This module contains the federation logic 5 | module Federation 6 | end 7 | end 8 | 9 | require "diaspora_federation/federation/diaspora_url_parser" 10 | require "diaspora_federation/federation/fetcher" 11 | require "diaspora_federation/federation/receiver" 12 | require "diaspora_federation/federation/sender" 13 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/diaspora_url_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | # This module is for parsing and fetching linked entities. 6 | module DiasporaUrlParser 7 | include Logging 8 | 9 | # Regex to find diaspora:// URLs 10 | DIASPORA_URL_REGEX = %r{ 11 | (?:web\+)?diaspora:// 12 | (#{Validation::Rule::DiasporaId::DIASPORA_ID_REGEX})/ 13 | (#{Entity::ENTITY_NAME_REGEX})/ 14 | (#{Validation::Rule::Guid::VALID_CHARS}) 15 | }ux.freeze 16 | 17 | # Parses all diaspora:// URLs from the text and fetches the entities from 18 | # the remote server if needed. 19 | # @param [String] sender the diaspora* ID of the sender of the entity 20 | # @param [String] text text with diaspora:// URLs to fetch 21 | def self.fetch_linked_entities(text) 22 | text.scan(DIASPORA_URL_REGEX).each do |author, type, guid| 23 | fetch_entity(author, type, guid) 24 | end 25 | end 26 | 27 | private_class_method def self.fetch_entity(author, type, guid) 28 | class_name = Entity.entity_class(type).to_s.rpartition("::").last 29 | return if DiasporaFederation.callbacks.trigger(:fetch_related_entity, class_name, guid) 30 | 31 | Fetcher.fetch_public(author, type, guid) 32 | rescue => e # rubocop:disable Style/RescueStandardError 33 | logger.error "Failed to fetch linked entity #{type}:#{guid}: #{e.class}: #{e.message}" 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/receiver.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | # This module parses and receives entities. 6 | module Receiver 7 | extend Logging 8 | 9 | # Receive a public message 10 | # @param [String] data message to receive 11 | def self.receive_public(data) 12 | magic_env_xml = Nokogiri::XML(data).root 13 | magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) 14 | Public.new(magic_env).receive 15 | rescue => e # rubocop:disable Style/RescueStandardError 16 | logger.error "failed to receive public message: #{e.class}: #{e.message}" 17 | logger.debug "received data:\n#{data}" 18 | raise e 19 | end 20 | 21 | # Receive a private message 22 | # @param [String] data message to receive 23 | # @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message 24 | # @param [Object] recipient_id the identifier to persist the entity for the correct user, 25 | # see +receive_entity+ callback 26 | def self.receive_private(data, recipient_private_key, recipient_id) 27 | raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA) 28 | 29 | magic_env_xml = Salmon::EncryptedMagicEnvelope.decrypt(data, recipient_private_key) 30 | magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) 31 | Private.new(magic_env, recipient_id).receive 32 | rescue => e # rubocop:disable Style/RescueStandardError 33 | logger.error "failed to receive private message for #{recipient_id}: #{e.class}: #{e.message}" 34 | logger.debug "received data:\n#{data}" 35 | raise e 36 | end 37 | end 38 | end 39 | end 40 | 41 | require "diaspora_federation/federation/receiver/exceptions" 42 | require "diaspora_federation/federation/receiver/abstract_receiver" 43 | require "diaspora_federation/federation/receiver/public" 44 | require "diaspora_federation/federation/receiver/private" 45 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/receiver/abstract_receiver.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | module Receiver 6 | # Common functionality for receivers 7 | class AbstractReceiver 8 | include Logging 9 | 10 | # Creates a new receiver 11 | # @param [MagicEnvelope] magic_envelope the received magic envelope 12 | # @param [Object] recipient_id the identifier of the recipient of a private message 13 | def initialize(magic_envelope, recipient_id=nil) 14 | @entity = magic_envelope.payload 15 | @sender = magic_envelope.sender 16 | @recipient_id = recipient_id 17 | end 18 | 19 | # Validates and receives the entity 20 | def receive 21 | validate_and_receive 22 | rescue => e # rubocop:disable Style/RescueStandardError 23 | logger.error "failed to receive #{entity}" 24 | raise e 25 | end 26 | 27 | private 28 | 29 | attr_reader :entity, :sender, :recipient_id 30 | 31 | def validate_and_receive 32 | validate 33 | fetch_linked_entities_from_text 34 | DiasporaFederation.callbacks.trigger(:receive_entity, entity, sender, recipient_id) 35 | logger.info "successfully received #{entity} from person #{sender}#{" for #{recipient_id}" if recipient_id}" 36 | end 37 | 38 | def validate 39 | raise InvalidSender, "invalid sender: #{sender}" unless sender_valid? 40 | end 41 | 42 | def sender_valid? 43 | if entity.respond_to?(:sender_valid?) 44 | entity.sender_valid?(sender) 45 | else 46 | sender == entity.author 47 | end 48 | end 49 | 50 | def fetch_linked_entities_from_text 51 | DiasporaUrlParser.fetch_linked_entities(entity.text) if entity.respond_to?(:text) && entity.text 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/receiver/exceptions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | module Receiver 6 | # Raised, if the sender of the {Salmon::MagicEnvelope} is not allowed to send the entity. 7 | class InvalidSender < RuntimeError 8 | end 9 | 10 | # Raised, if receiving a private message without recipient. 11 | class RecipientRequired < RuntimeError 12 | end 13 | 14 | # Raised, if receiving a message with public receiver which is not public but should be. 15 | class NotPublic < RuntimeError 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/receiver/private.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | module Receiver 6 | # Receiver for private entities 7 | class Private < AbstractReceiver 8 | private 9 | 10 | def validate 11 | raise RecipientRequired if recipient_id.nil? 12 | 13 | super 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/receiver/public.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | module Receiver 6 | # Receiver for public entities 7 | class Public < AbstractReceiver 8 | private 9 | 10 | def validate 11 | super 12 | validate_public_flag 13 | end 14 | 15 | def validate_public_flag 16 | return if !entity.respond_to?(:public) || entity.public 17 | 18 | if entity.is_a?(Entities::Profile) && 19 | %i[bio birthday gender location].all? {|prop| entity.public_send(prop).nil? } 20 | return 21 | end 22 | 23 | raise NotPublic, "received entity #{entity} should be public!" 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/diaspora_federation/federation/sender.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Federation 5 | # Federation logic to send messages to other pods 6 | module Sender 7 | # Send a public message to all urls 8 | # 9 | # @param [String] sender_id sender diaspora-ID 10 | # @param [String] obj_str object string representation for logging (e.g. type@guid) 11 | # @param [Array] urls receive-urls from pods 12 | # @param [String] xml salmon-xml 13 | # @return [Array] url to retry 14 | def self.public(sender_id, obj_str, urls, xml) 15 | hydra = HydraWrapper.new(sender_id, obj_str) 16 | urls.each {|url| hydra.insert_magic_env_request(url, xml) } 17 | hydra.send 18 | end 19 | 20 | # Send a private message to receive-urls 21 | # 22 | # @param [String] sender_id sender diaspora-ID 23 | # @param [String] obj_str object string representation for logging (e.g. type@guid) 24 | # @param [Hash] targets Hash with receive-urls (key) of peoples with encrypted salmon-xml for them (value) 25 | # @return [Hash] targets to retry 26 | def self.private(sender_id, obj_str, targets) 27 | hydra = HydraWrapper.new(sender_id, obj_str) 28 | targets.each {|url, json| hydra.insert_enc_magic_env_request(url, json) } 29 | hydra.send.to_h {|url| [url, targets[url]] } 30 | end 31 | end 32 | end 33 | end 34 | 35 | require "diaspora_federation/federation/sender/hydra_wrapper" 36 | -------------------------------------------------------------------------------- /lib/diaspora_federation/http_client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "faraday" 4 | require "faraday/follow_redirects" 5 | 6 | module DiasporaFederation 7 | # A wrapper for {https://github.com/lostisland/faraday Faraday} 8 | # 9 | # @see Discovery::Discovery 10 | # @see Federation::Fetcher 11 | class HttpClient 12 | # Perform a GET request 13 | # 14 | # @param [String] uri the URI 15 | # @return [Faraday::Response] the response 16 | def self.get(uri) 17 | connection.get(uri) 18 | end 19 | 20 | # Gets the Faraday connection 21 | # 22 | # @return [Faraday::Connection] the response 23 | def self.connection 24 | create_default_connection unless @connection 25 | @connection.dup 26 | end 27 | 28 | private_class_method def self.create_default_connection 29 | options = { 30 | request: {timeout: DiasporaFederation.http_timeout}, 31 | ssl: {ca_file: DiasporaFederation.certificate_authorities} 32 | } 33 | 34 | @connection = Faraday::Connection.new(options) do |builder| 35 | builder.use Faraday::FollowRedirects::Middleware, limit: DiasporaFederation.http_redirect_limit 36 | 37 | builder.adapter Faraday.default_adapter 38 | end 39 | 40 | @connection.headers["User-Agent"] = DiasporaFederation.http_user_agent 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/diaspora_federation/logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # Logging module for the diaspora* federation 5 | # 6 | # It uses the logging-gem if available. 7 | module Logging 8 | # Add +logger+ also as class method when included 9 | # @param [Class] klass the class into which the module is included 10 | def self.included(klass) 11 | klass.extend(self) 12 | end 13 | 14 | private 15 | 16 | # Get the logger for this class 17 | # 18 | # Use the logging-gem if available, else use a default logger. 19 | def logger 20 | @logger ||= if defined?(::Logging::Logger) 21 | # Use logging-gem if available 22 | ::Logging::Logger[self] 23 | elsif defined?(::Rails) 24 | # Use rails logger if running in rails and no logging-gem is available 25 | ::Rails.logger 26 | else 27 | # fallback logger 28 | @logger = Logger.new($stdout) 29 | @logger.level = Logger::INFO 30 | @logger 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/diaspora_federation/parsers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # This namespace contains parsers which are used to deserialize federation entities 5 | # objects from supported formats (XML, JSON) to objects of DiasporaFederation::Entity 6 | # classes 7 | module Parsers 8 | end 9 | end 10 | 11 | require "diaspora_federation/parsers/base_parser" 12 | require "diaspora_federation/parsers/json_parser" 13 | require "diaspora_federation/parsers/xml_parser" 14 | require "diaspora_federation/parsers/relayable_json_parser" 15 | require "diaspora_federation/parsers/relayable_xml_parser" 16 | -------------------------------------------------------------------------------- /lib/diaspora_federation/parsers/relayable_json_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Parsers 5 | # This is a parser of JSON serialized object, that is normally used for parsing data of relayables. 6 | # Assumed format differs from the usual entity by additional "property_order" property which is used to 7 | # compute signatures deterministically. 8 | # Input JSON for this parser is expected to match "/definitions/relayable" subschema of the JSON schema at 9 | # https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json. 10 | class RelayableJsonParser < JsonParser 11 | # @see JsonParser#parse 12 | # @see BaseParser#parse 13 | # @return [Array[2]] comprehensive data for an entity instantiation 14 | def parse(json_hash) 15 | super.push(json_hash["property_order"]) 16 | end 17 | 18 | private 19 | 20 | def from_json_sanity_validation(json_hash) 21 | super 22 | return unless json_hash["property_order"].nil? 23 | 24 | raise DeserializationError, "Required property is missing in JSON object: property_order" 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/diaspora_federation/parsers/relayable_xml_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Parsers 5 | # This is a parser of XML serialized object that is normally used for parsing data of relayables. 6 | # Explanations about the XML data format can be found 7 | # {https://diaspora.github.io/diaspora_federation/federation/xml_serialization.html here}. 8 | # Specific features of relayables are described 9 | # {https://diaspora.github.io/diaspora_federation/federation/relayable.html here}. 10 | # 11 | # @see https://diaspora.github.io/diaspora_federation/federation/xml_serialization.html XML Serialization 12 | # documentation 13 | # @see https://diaspora.github.io/diaspora_federation/federation/relayable.html Relayable documentation 14 | class RelayableXmlParser < XmlParser 15 | # @see XmlParser#parse 16 | # @see BaseParser#parse 17 | # @return [Array[2]] comprehensive data for an entity instantiation 18 | def parse(*args) 19 | hash = super[0] 20 | [hash, hash.keys] 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/diaspora_federation/rails.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "diaspora_federation/engine" 4 | 5 | require "diaspora_federation" 6 | 7 | module DiasporaFederation 8 | # diaspora* federation rails engine 9 | module Rails 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/diaspora_federation/salmon.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # This module contains a diaspora*-specific implementation of parts of the 5 | # {http://www.salmon-protocol.org/ Salmon Protocol}. 6 | module Salmon 7 | # XML namespace url 8 | XMLNS = "https://joindiaspora.com/protocol" 9 | end 10 | end 11 | 12 | require "base64" 13 | 14 | require "diaspora_federation/salmon/aes" 15 | require "diaspora_federation/salmon/exceptions" 16 | require "diaspora_federation/salmon/magic_envelope" 17 | require "diaspora_federation/salmon/encrypted_magic_envelope" 18 | -------------------------------------------------------------------------------- /lib/diaspora_federation/salmon/exceptions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Salmon 5 | # Raised, if failed to fetch the public key of the sender of the received message 6 | class SenderKeyNotFound < RuntimeError 7 | end 8 | 9 | # Raised, if the Magic Envelope XML structure is malformed. 10 | class InvalidEnvelope < RuntimeError 11 | end 12 | 13 | # Raised, if the calculated signature doesn't match the one contained in the 14 | # Magic Envelope. 15 | class InvalidSignature < RuntimeError 16 | end 17 | 18 | # Raised, if the parsed Magic Envelope specifies an unhandled data type. 19 | class InvalidDataType < RuntimeError 20 | end 21 | 22 | # Raised, if the parsed Magic Envelope specifies an unhandled algorithm. 23 | class InvalidAlgorithm < RuntimeError 24 | end 25 | 26 | # Raised, if the parsed Magic Envelope specifies an unhandled encoding. 27 | class InvalidEncoding < RuntimeError 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/diaspora_federation/schemas.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "pathname" 4 | require "json" 5 | 6 | module DiasporaFederation 7 | # A helper class to access the JSON schema. 8 | module Schemas 9 | # federation_entities schema uri 10 | FEDERATION_ENTITIES_URI = "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json" 11 | 12 | # Parsed federation_entities schema 13 | def self.federation_entities 14 | @federation_entities ||= JSON.parse( 15 | Pathname.new(__dir__).join("schemas", "federation_entities.json").read 16 | ) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/diaspora_federation/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "fabrication" 4 | 5 | require "diaspora_federation" 6 | require "diaspora_federation/test/entity_generator" 7 | 8 | module DiasporaFederation 9 | # This module encapsulates helper functions maybe wanted by a testsuite of a diaspora_federation gem user application. 10 | module Test 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/diaspora_federation/test/entity_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Test 5 | # Generator to instantiate entities 6 | class EntityGenerator < Fabrication::Generator::Base 7 | def self.supports?(klass) 8 | klass.ancestors.include?(DiasporaFederation::Entity) 9 | end 10 | 11 | def build_instance 12 | self._instance = resolved_class.new(_attributes) 13 | end 14 | 15 | def to_hash(attributes=[], _callbacks=[]) 16 | process_attributes(attributes) 17 | _attributes.transform_keys(&:to_sym) 18 | end 19 | end 20 | 21 | Fabrication.configure do |config| 22 | config.generators << EntityGenerator 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/account_deletion_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::AccountDeletion}. 6 | class AccountDeletionValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/account_migration_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::AccountMigration}. 6 | class AccountMigrationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | 11 | rule :profile, :not_nil 12 | 13 | rule :old_identity, :diaspora_id 14 | 15 | rule :remote_photo_path, URI: [:path] 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/comment_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Comment}. 6 | class CommentValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | include RelayableValidator 10 | 11 | rule :text, [:not_empty, 12 | length: {maximum: 65_535}] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/contact_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Contact}. 6 | class ContactValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | rule :recipient, :diaspora_id 11 | rule :following, :boolean 12 | rule :sharing, :boolean 13 | rule :blocking, :boolean 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/conversation_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Conversation}. 6 | class ConversationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | rule :guid, :guid 11 | 12 | rule :subject, [:not_empty, length: {maximum: 255}] 13 | 14 | rule :participants, [:not_empty, diaspora_id_list: {minimum: 2}] 15 | rule :messages, :not_nil 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/embed_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Embed}. 6 | class EmbedValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :url, :URI 10 | rule :title, length: {maximum: 255} 11 | rule :description, length: {maximum: 65_535} 12 | rule :image, URI: %i[host path] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/event_participation_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::EventParticipation}. 6 | class EventParticipationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | include RelayableValidator 10 | 11 | rule :status, [:not_empty, regular_expression: {regex: /\A(accepted|declined|tentative)\z/}] 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/event_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Event}. 6 | class EventValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | 11 | rule :guid, :guid 12 | 13 | rule :summary, [:not_empty, length: {maximum: 255}] 14 | rule :description, length: {maximum: 65_535} 15 | 16 | rule :start, :not_nil 17 | 18 | rule :all_day, :boolean 19 | 20 | rule :timezone, regular_expression: {regex: %r{\A[A-Za-z_-]{,14}(/[A-Za-z_-]{,14}){1,2}\z}} 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/h_card_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Discovery::HCard}. 6 | # 7 | # @note 8 | class HCardValidator < OptionalAwareValidator 9 | include Validation 10 | 11 | rule :guid, :guid 12 | 13 | # The name must not contain a semicolon because of mentions. 14 | # @{ ; } 15 | rule :full_name, regular_expression: {regex: /\A[^;]{,70}\z/} 16 | rule :first_name, regular_expression: {regex: /\A[^;]{,32}\z/} 17 | rule :last_name, regular_expression: {regex: /\A[^;]{,32}\z/} 18 | 19 | # this urls can be relative 20 | rule :photo_large_url, [:not_nil, URI: [:path]] 21 | rule :photo_medium_url, [:not_nil, URI: [:path]] 22 | rule :photo_small_url, [:not_nil, URI: [:path]] 23 | 24 | rule :public_key, :public_key 25 | 26 | rule :searchable, :boolean 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/like_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Like}. 6 | class LikeValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | include RelayableValidator 10 | 11 | rule :parent_type, [:not_empty, regular_expression: {regex: /\A(Post|Comment)\z/}] 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/location_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Location}. 6 | class LocationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :lat, :not_empty 10 | rule :lng, :not_empty 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/message_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Message}. 6 | class MessageValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | rule :guid, :guid 11 | rule :conversation_guid, :guid 12 | 13 | rule :text, [:not_empty, 14 | length: {maximum: 65_535}] 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/optional_aware_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # Abstract validator which only validates optional fields when they are not nil. 6 | class OptionalAwareValidator < Validation::Validator 7 | def rules 8 | super.reject do |field, rules| 9 | @obj.public_send(field).nil? && 10 | !rules.map(&:class).include?(Validation::Rule::NotNil) && 11 | optional_props.include?(field) 12 | end 13 | end 14 | 15 | private 16 | 17 | def optional_props 18 | return [] unless @obj.class.respond_to?(:optional_props) 19 | 20 | @obj.class.optional_props 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/participation_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Participation}. 6 | class ParticipationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | rule :guid, :guid 11 | rule :parent_guid, :guid 12 | rule :parent_type, [:not_empty, regular_expression: {regex: /\APost\z/}] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/person_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Person}. 6 | class PersonValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :guid, :guid 10 | 11 | rule :author, :diaspora_id 12 | 13 | rule :url, %i[not_nil URI] 14 | 15 | rule :profile, :not_nil 16 | 17 | rule :exported_key, :public_key 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/photo_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Photo}. 6 | class PhotoValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :guid, :guid 10 | 11 | rule :author, :diaspora_id 12 | 13 | rule :public, :boolean 14 | 15 | rule :remote_photo_path, [:not_empty, URI: [:path]] 16 | 17 | rule :remote_photo_name, :not_empty 18 | 19 | rule :status_message_guid, :guid 20 | 21 | rule :text, length: {maximum: 65_535} 22 | 23 | rule :height, :numeric 24 | 25 | rule :width, :numeric 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/poll_answer_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::PollAnswer}. 6 | class PollAnswerValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :guid, :guid 10 | rule :answer, [:not_empty, length: {maximum: 255}] 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/poll_participation_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::PollParticipation}. 6 | class PollParticipationValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | include RelayableValidator 10 | 11 | rule :poll_answer_guid, :guid 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/poll_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Poll}. 6 | class PollValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :guid, :guid 10 | rule :question, [:not_empty, length: {maximum: 255}] 11 | rule :poll_answers, length: {minimum: 2} 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/profile_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Profile}. 6 | class ProfileValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | 11 | # The name must not contain a semicolon because of mentions. 12 | # @{ ; } 13 | rule :full_name, regular_expression: {regex: /\A[^;]{,70}\z/} 14 | rule :first_name, regular_expression: {regex: /\A[^;]{,32}\z/} 15 | rule :last_name, regular_expression: {regex: /\A[^;]{,32}\z/} 16 | 17 | # These urls can be relative. 18 | rule :image_url, URI: [:path] 19 | rule :image_url_medium, URI: [:path] 20 | rule :image_url_small, URI: [:path] 21 | 22 | rule :birthday, :birthday 23 | 24 | rule :gender, length: {maximum: 255} 25 | rule :bio, length: {maximum: 65_535} 26 | rule :location, length: {maximum: 255} 27 | 28 | rule :searchable, :boolean 29 | rule :public, :boolean 30 | rule :nsfw, :boolean 31 | 32 | rule :tag_string, tag_count: {maximum: 5} 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/related_entity_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::RelatedEntity}. 6 | class RelatedEntityValidator < Validation::Validator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | rule :local, :boolean 11 | rule :public, :boolean 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/relayable_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This is included to validatros which validate entities which include {Entities::Relayable}. 6 | module RelayableValidator 7 | # When this module is included in a Validator child it adds rules for relayable validation. 8 | # @param [Validation::Validator] validator the validator in which it is included 9 | def self.included(validator) 10 | validator.class_eval do 11 | rule :author, :diaspora_id 12 | rule :guid, :guid 13 | rule :parent_guid, :guid 14 | rule :parent, :not_nil 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/reshare_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Reshare}. 6 | class ReshareValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :root_author, :diaspora_id 10 | 11 | rule :root_guid, :guid 12 | 13 | rule :author, :diaspora_id 14 | 15 | rule :guid, :guid 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/retraction_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::Retraction}. 6 | class RetractionValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | 11 | rule :target_guid, :guid 12 | rule :target_type, :not_empty 13 | rule :target, :not_nil 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/birthday.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "date" 4 | 5 | module Validation 6 | module Rule 7 | # Birthday validation rule 8 | # 9 | # Valid is: 10 | # * nil or an empty +String+ 11 | # * a +Date+ object 12 | # * a +String+ with the format "yyyy-mm-dd" and is a valid +Date+, example: 2015-07-25 13 | class Birthday 14 | # The error key for this rule 15 | # @return [Symbol] error key 16 | def error_key 17 | :birthday 18 | end 19 | 20 | # Determines if value is a valid birthday date. 21 | def valid_value?(value) 22 | return true if value.nil? || (value.is_a?(String) && value.empty?) 23 | return true if value.is_a? Date 24 | 25 | if value.is_a?(String) && value.match?(/[0-9]{4}-[0-9]{2}-[0-9]{2}/) 26 | date_field = value.split("-").map(&:to_i) 27 | return Date.valid_civil?(date_field[0], date_field[1], date_field[2]) 28 | end 29 | 30 | false 31 | end 32 | 33 | # This rule has no params. 34 | # @return [Hash] params 35 | def params 36 | {} 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/boolean.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # Boolean validation rule 6 | # 7 | # Valid is: 8 | # * a +String+: "true", "false", "t", "f", "yes", "no", "y", "n", "1", "0" 9 | # * a +Integer+: 1 or 0 10 | # * a +Boolean+: true or false 11 | class Boolean 12 | # The error key for this rule 13 | # @return [Symbol] error key 14 | def error_key 15 | :boolean 16 | end 17 | 18 | # Determines if value is a valid +boolean+ 19 | def valid_value?(value) 20 | return false if value.nil? 21 | 22 | case value 23 | when String 24 | true if value =~ /\A(true|false|t|f|yes|no|y|n|1|0)\z/i 25 | when Integer 26 | true if [1, 0].include?(value) 27 | else 28 | [true, false].include?(value) 29 | end 30 | end 31 | 32 | # This rule has no params. 33 | # @return [Hash] params 34 | def params 35 | {} 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/diaspora_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # diaspora* ID validation rule 6 | # 7 | # A simple rule to validate the base structure of diaspora* IDs. 8 | class DiasporaId 9 | # Maximum length of a full diaspora* ID 10 | DIASPORA_ID_MAX_LENGTH = 255 11 | 12 | # The Regex for a valid diaspora* ID 13 | DIASPORA_ID_REGEX = begin 14 | username = "[[:lower:]\\d\\-\\.\\_]+" 15 | hostname_part = "[[:lower:]\\d\\-]" 16 | hostname = "#{hostname_part}+(?:[.]#{hostname_part}*)*" 17 | ipv4 = "(?:[\\d]{1,3}\\.){3}[\\d]{1,3}" 18 | ipv6 = "\\[(?:[[:xdigit:]]{0,4}:){0,7}[[:xdigit:]]{1,4}\\]" 19 | ip_addr = "(?:#{ipv4}|#{ipv6})" 20 | domain = "(?:#{hostname}|#{ip_addr})" 21 | port = "(?::[\\d]+)?" 22 | 23 | "#{username}\\@#{domain}#{port}" 24 | end 25 | 26 | # The Regex for validating a full diaspora* ID 27 | DIASPORA_ID = /\A#{DIASPORA_ID_REGEX}\z/u.freeze 28 | 29 | # The error key for this rule 30 | # @return [Symbol] error key 31 | def error_key 32 | :diaspora_id 33 | end 34 | 35 | # Determines if value is a valid diaspora* ID 36 | def valid_value?(value) 37 | return false unless value.is_a?(String) 38 | return false if value.length > DIASPORA_ID_MAX_LENGTH 39 | 40 | value =~ DIASPORA_ID 41 | end 42 | 43 | # This rule has no params. 44 | # @return [Hash] params 45 | def params 46 | {} 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/diaspora_id_list.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # Rule for validating the number of diaspora* IDs in a string. 6 | # The evaluated string is split at ";". 7 | class DiasporaIdList 8 | # This rule can have a +minimum+ or +maximum+ param. 9 | # @return [Hash] params 10 | attr_reader :params 11 | 12 | # Creates a new rule for a diaspora* ID list validation 13 | # @param [Hash] params 14 | # @option params [Integer] :minimum minimum allowed id count 15 | # @option params [Integer] :maximum maximum allowed id count 16 | def initialize(params={}) 17 | %i[minimum maximum].each do |param| 18 | if params.include?(param) && !params[param].is_a?(Integer) 19 | raise ArgumentError, "The :#{param} needs to be an Integer" 20 | end 21 | end 22 | 23 | @params = params 24 | end 25 | 26 | # The error key for this rule 27 | # @return [Symbol] error key 28 | def error_key 29 | :diaspora_id_list 30 | end 31 | 32 | def valid_value?(value) 33 | ids = value.split(";") 34 | return false if params.include?(:maximum) && ids.count > params[:maximum] 35 | return false if params.include?(:minimum) && ids.count < params[:minimum] 36 | 37 | ids.each do |id| 38 | return false if DiasporaId::DIASPORA_ID.match(id).nil? 39 | end 40 | true 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/guid.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # GUID validation rule 6 | # 7 | # Valid is a +String+ that is at least 16 and at most 255 chars long. It contains only: 8 | # * Letters: a-z 9 | # * Numbers: 0-9 10 | # * Special chars: '-', '_', '@', '.' and ':' 11 | # Special chars aren't allowed at the end. 12 | class Guid 13 | # Allowed chars to validate a GUID with a regex 14 | VALID_CHARS = "[0-9A-Za-z\\-_@.:]{15,254}[0-9A-Za-z]" 15 | 16 | # The error key for this rule 17 | # @return [Symbol] error key 18 | def error_key 19 | :guid 20 | end 21 | 22 | # Determines if value is a valid +GUID+ 23 | def valid_value?(value) 24 | value.is_a?(String) && value =~ /\A#{VALID_CHARS}\z/ 25 | end 26 | 27 | # This rule has no params. 28 | # @return [Hash] params 29 | def params 30 | {} 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/not_nil.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # Validates that a property is not +nil+ 6 | class NotNil 7 | # The error key for this rule 8 | # @return [Symbol] error key 9 | def error_key 10 | :not_nil 11 | end 12 | 13 | # Determines if value is not nil 14 | def valid_value?(value) 15 | !value.nil? 16 | end 17 | 18 | # This rule has no params. 19 | # @return [Hash] params 20 | def params 21 | {} 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/public_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # Public key validation rule 6 | # 7 | # A valid key must: 8 | # * start with "-----BEGIN PUBLIC KEY-----" and end with "-----END PUBLIC KEY-----" 9 | # or 10 | # * start with "-----BEGIN RSA PUBLIC KEY-----" and end with "-----END RSA PUBLIC KEY-----" 11 | class PublicKey 12 | # The error key for this rule 13 | # @return [Symbol] error key 14 | def error_key 15 | :public_key 16 | end 17 | 18 | # Determines if value is a valid public key 19 | def valid_value?(value) 20 | !value.nil? && ( 21 | (value.strip.start_with?("-----BEGIN PUBLIC KEY-----") && 22 | value.strip.end_with?("-----END PUBLIC KEY-----")) || 23 | (value.strip.start_with?("-----BEGIN RSA PUBLIC KEY-----") && 24 | value.strip.end_with?("-----END RSA PUBLIC KEY-----")) 25 | ) 26 | end 27 | 28 | # This rule has no params. 29 | # @return [Hash] params 30 | def params 31 | {} 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/rules/tag_count.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Validation 4 | module Rule 5 | # Rule for validating the number of tags in a string. 6 | # Only the "#" characters will be counted. 7 | # The string can be nil. 8 | class TagCount 9 | # This rule must have a +maximum+ param. 10 | # @return [Hash] params 11 | attr_reader :params 12 | 13 | # Creates a new rule for a maximum tag count validation 14 | # @param [Hash] params 15 | # @option params [Integer] :maximum maximum allowed tag count 16 | def initialize(params) 17 | unless params.include?(:maximum) && params[:maximum].is_a?(Integer) 18 | raise ArgumentError, "A number has to be specified for :maximum" 19 | end 20 | 21 | @params = params 22 | end 23 | 24 | # The error key for this rule 25 | # @return [Symbol] error key 26 | def error_key 27 | :tag_count 28 | end 29 | 30 | # Determines if value doesn't have more than +maximum+ tags 31 | def valid_value?(value) 32 | value.nil? || value.count("#") <= params[:maximum] 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/status_message_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Entities::StatusMessage}. 6 | class StatusMessageValidator < OptionalAwareValidator 7 | include Validation 8 | 9 | rule :author, :diaspora_id 10 | 11 | rule :guid, :guid 12 | 13 | rule :text, length: {maximum: 65_535} 14 | 15 | rule :photos, :not_nil 16 | 17 | rule :public, :boolean 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/diaspora_federation/validators/web_finger_validator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | module Validators 5 | # This validates a {Discovery::WebFinger}. 6 | # 7 | # @note It does not validate the guid and public key, because it will be 8 | # removed in the webfinger. 9 | class WebFingerValidator < OptionalAwareValidator 10 | include Validation 11 | 12 | rule :acct_uri, :not_empty 13 | 14 | rule :hcard_url, [:not_nil, URI: %i[host path]] 15 | rule :seed_url, %i[not_nil URI] 16 | rule :profile_url, URI: %i[host path] 17 | rule :atom_url, URI: %i[host path] 18 | rule :salmon_url, URI: %i[host path] 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/diaspora_federation/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | # the gem version 5 | VERSION = "1.1.0" 6 | end 7 | -------------------------------------------------------------------------------- /lib/tasks/build.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | desc "Build gem into the pkg directory" 4 | task build: :test do 5 | FileUtils.rm_rf("pkg") 6 | Dir["*.gemspec"].each do |gemspec| 7 | system "gem build #{gemspec}" 8 | end 9 | FileUtils.mkdir_p("pkg") 10 | FileUtils.mv(Dir["*.gem"], "pkg") 11 | 12 | Rake::Task["update_json_schemas"].invoke 13 | end 14 | 15 | desc "Update JSON schemas for github-pages" 16 | task :update_json_schemas do 17 | git_clean = `git status --porcelain`.empty? 18 | sh "git stash" unless git_clean 19 | 20 | FileUtils.mkdir_p("docs/schemas") 21 | FileUtils.cp(Dir["lib/diaspora_federation/schemas/*.json"], "docs/schemas") 22 | 23 | sh "git add docs/schemas && git diff --staged --quiet || git commit -m 'Update JSON schemas for github-pages'" 24 | sh "git stash pop" unless git_clean 25 | end 26 | 27 | desc "Tags version, pushes to remote, and pushes gem" 28 | task release: :build do 29 | Dir["pkg/diaspora_federation-*-*.gem"].each do |gem| 30 | sh "gem push #{gem}" 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/controllers/diaspora_federation/application_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe ApplicationController, type: :controller do 5 | controller do 6 | def index 7 | head :ok 8 | end 9 | end 10 | 11 | describe "#set_locale" do 12 | it "sets the default locale" do 13 | expect(I18n).to receive(:locale=).with(:en) 14 | get :index 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/diaspora_federation/h_card_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe HCardController, type: :controller do 5 | routes { DiasporaFederation::Engine.routes } 6 | 7 | describe "GET #hcard" do 8 | it "succeeds when the person exists" do 9 | get :hcard, params: {guid: alice.guid} 10 | expect(response).to be_successful 11 | end 12 | 13 | it "contains the guid" do 14 | get :hcard, params: {guid: alice.guid} 15 | expect(response.body).to include "#{alice.guid}" 16 | end 17 | 18 | it "contains the username" do 19 | get :hcard, params: {guid: alice.guid} 20 | expect(response.body).to include "alice" 21 | end 22 | 23 | it "404s when the person does not exist" do 24 | get :hcard, params: {guid: "unknown_guid"} 25 | expect(response).to be_not_found 26 | end 27 | 28 | it "calls the fetch_person_for_hcard callback" do 29 | expect_callback(:fetch_person_for_hcard, alice.guid).and_call_original 30 | 31 | get :hcard, params: {guid: alice.guid} 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/controllers/diaspora_federation/receive_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe ReceiveController, type: :controller do 5 | routes { DiasporaFederation::Engine.routes } 6 | 7 | describe "POST #public" do 8 | context "magic envelope" do 9 | before do 10 | Mime::Type.register("application/magic-envelope+xml", :magic_envelope) 11 | @request.env["CONTENT_TYPE"] = "application/magic-envelope+xml" 12 | end 13 | 14 | it "returns a 202 if queued correctly" do 15 | expect_callback(:queue_public_receive, "") 16 | 17 | post :public, body: +"" 18 | expect(response.code).to eq("202") 19 | end 20 | end 21 | end 22 | 23 | describe "POST #private" do 24 | context "encrypted magic envelope" do 25 | before do 26 | @request.env["CONTENT_TYPE"] = "application/json" 27 | end 28 | 29 | it "return a 404 if not queued successfully (unknown user guid)" do 30 | expect_callback( 31 | :queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}" 32 | ).and_return(false) 33 | 34 | post :private, 35 | body: +"{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", 36 | params: {guid: "any-guid"} 37 | expect(response.code).to eq("404") 38 | end 39 | 40 | it "returns a 202 if the callback returned true" do 41 | expect_callback( 42 | :queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}" 43 | ).and_return(true) 44 | 45 | post :private, 46 | body: +"{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", 47 | params: {guid: "any-guid"} 48 | expect(response.code).to eq("202") 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "diaspora_federation/test/factories" 4 | 5 | Fabricator(:person) do 6 | diaspora_id { Fabricate.sequence(:diaspora_id) } 7 | url "http://somehost:3000/" 8 | serialized_public_key { Fabricate.sequence(:public_key) } 9 | end 10 | 11 | Fabricator(:user, class_name: Person) do 12 | diaspora_id { Fabricate.sequence(:diaspora_id) } 13 | url "http://localhost:3000/" 14 | after_build do |user| 15 | private_key = OpenSSL::PKey::RSA.generate(1024) 16 | user.serialized_private_key = private_key.export 17 | user.serialized_public_key = private_key.public_key.export 18 | end 19 | end 20 | 21 | Fabricator(:post, class_name: Entity) do 22 | initialize_with { resolved_class.new("Post") } 23 | author { Fabricate(:person) } 24 | end 25 | 26 | Fabricator(:comment, class_name: Entity) do 27 | initialize_with { resolved_class.new("Comment") } 28 | author { Fabricate(:person) } 29 | end 30 | 31 | Fabricator(:poll, class_name: Entity) do 32 | initialize_with { resolved_class.new("Poll") } 33 | author { Fabricate(:person) } 34 | end 35 | 36 | Fabricator(:event, class_name: Entity) do 37 | initialize_with { resolved_class.new("Event") } 38 | author { Fabricate(:person) } 39 | end 40 | 41 | Fabricator(:conversation, class_name: Entity) do 42 | initialize_with { resolved_class.new("Conversation") } 43 | author { Fabricate(:person) } 44 | end 45 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/account_deletion_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::AccountDeletion do 5 | let(:data) { Fabricate.attributes_for(:account_deletion_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:author]} 10 | 11 | XML 12 | 13 | let(:string) { "AccountDeletion:#{data[:author]}" } 14 | 15 | it_behaves_like "an Entity subclass" 16 | 17 | it_behaves_like "an XML Entity" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/account_migration/signable_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::AccountMigration::Signable do 5 | let(:entity) { TestAMSignableEntity.new({}) } 6 | 7 | class TestAMSignableEntity < Entity 8 | include Entities::AccountMigration::Signable 9 | 10 | property :my_signature, :string, default: nil 11 | 12 | def old_identity 13 | "old" 14 | end 15 | 16 | def new_identity 17 | "new" 18 | end 19 | end 20 | 21 | it_behaves_like "a signable" do 22 | let(:test_class) { TestAMSignableEntity } 23 | let(:test_string) { "AccountMigration:old:new" } 24 | end 25 | 26 | describe "#unique_migration_descriptor" do 27 | it "composes a string using #old_identity and #new_identity" do 28 | expect(entity.unique_migration_descriptor).to eq("AccountMigration:old:new") 29 | end 30 | end 31 | 32 | describe "#signature_data" do 33 | it "delegates to #unique_migration_descriptor" do 34 | expect(entity.signature_data).to eq("AccountMigration:old:new") 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/comment_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Comment do 5 | let(:parent) { Fabricate(:post, author: bob) } 6 | let(:parent_entity) { Fabricate(:related_entity, author: bob.diaspora_id) } 7 | let(:data) { 8 | Fabricate 9 | .attributes_for( 10 | :comment_entity, 11 | author: alice.diaspora_id, 12 | parent_guid: parent.guid, 13 | parent: parent_entity 14 | ).tap {|hash| add_signatures(hash) } 15 | } 16 | 17 | let(:xml) { <<~XML } 18 | 19 | #{data[:author]} 20 | #{data[:guid]} 21 | #{parent.guid} 22 | #{data[:text]} 23 | #{data[:created_at].utc.iso8601} 24 | #{data[:edited_at].utc.iso8601} 25 | #{data[:author_signature]} 26 | 27 | XML 28 | 29 | let(:json) { <<~JSON } 30 | { 31 | "entity_type": "comment", 32 | "entity_data": { 33 | "author": "#{data[:author]}", 34 | "guid": "#{data[:guid]}", 35 | "parent_guid": "#{parent.guid}", 36 | "author_signature": "#{data[:author_signature]}", 37 | "text": "#{data[:text]}", 38 | "created_at": "#{data[:created_at].iso8601}", 39 | "edited_at": "#{data[:edited_at].iso8601}" 40 | }, 41 | "property_order": [ 42 | "author", 43 | "guid", 44 | "parent_guid", 45 | "text", 46 | "created_at", 47 | "edited_at" 48 | ] 49 | } 50 | JSON 51 | 52 | let(:string) { "Comment:#{data[:guid]}:#{parent.guid}" } 53 | 54 | it_behaves_like "an Entity subclass" 55 | 56 | it_behaves_like "an XML Entity", [:created_at] 57 | 58 | it_behaves_like "a JSON Entity" 59 | 60 | it_behaves_like "a relayable Entity" 61 | 62 | it_behaves_like "a relayable JSON entity" 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/contact_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Contact do 5 | let(:data) { Fabricate.attributes_for(:contact_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:author]} 10 | #{data[:recipient]} 11 | #{data[:following]} 12 | #{data[:sharing]} 13 | #{data[:blocking]} 14 | 15 | XML 16 | 17 | let(:string) { "Contact:#{data[:author]}:#{data[:recipient]}" } 18 | 19 | it_behaves_like "an Entity subclass" 20 | 21 | it_behaves_like "an XML Entity" 22 | 23 | describe "#validate" do 24 | it "allows 'following' and 'sharing' to be true" do 25 | combinations = [ 26 | {following: true, sharing: true, blocking: false}, 27 | {following: true, sharing: false, blocking: false}, 28 | {following: false, sharing: true, blocking: false} 29 | ] 30 | combinations.each do |combination| 31 | expect { Entities::Contact.new(data.merge(combination)) }.not_to raise_error 32 | end 33 | end 34 | 35 | it "allows 'blocking' to be true" do 36 | expect { 37 | Entities::Contact.new(data.merge(following: false, sharing: false, blocking: true)) 38 | }.not_to raise_error 39 | end 40 | 41 | it "doesn't allow 'following'/'sharing' and 'blocking' to be true" do 42 | combinations = [ 43 | {following: true, sharing: true, blocking: true}, 44 | {following: true, sharing: false, blocking: true}, 45 | {following: false, sharing: true, blocking: true} 46 | ] 47 | combinations.each do |combination| 48 | expect { Entities::Contact.new(data.merge(combination)) }.to raise_error Entity::ValidationError 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/embed_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Embed do 5 | let(:data) { Fabricate.attributes_for(:embed_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:url]} 10 | #{data[:title]} 11 | #{data[:description]} 12 | #{data[:image]} 13 | 14 | XML 15 | 16 | let(:json) { <<~JSON } 17 | { 18 | "entity_type": "embed", 19 | "entity_data": { 20 | "url": "#{data[:url]}", 21 | "title": "#{data[:title]}", 22 | "description": "#{data[:description]}", 23 | "image": "#{data[:image]}" 24 | } 25 | } 26 | JSON 27 | 28 | let(:string) { "Embed:#{data[:url]}" } 29 | 30 | it_behaves_like "an Entity subclass" 31 | 32 | it_behaves_like "an XML Entity" 33 | 34 | it_behaves_like "a JSON Entity" 35 | 36 | describe "#validate" do 37 | it "allows 'url' to be set if 'nothing' is not true" do 38 | expect { Entities::Embed.new(data) }.not_to raise_error 39 | end 40 | 41 | it "allows 'url' to be missing if 'nothing' is true" do 42 | expect { Entities::Embed.new(nothing: true) }.not_to raise_error 43 | end 44 | 45 | it "doesn't allow 'url' to be set if 'nothing' is true" do 46 | expect { 47 | Entities::Embed.new(data.merge(nothing: true)) 48 | }.to raise_error Entity::ValidationError, "Either 'url' must be set or 'nothing' must be 'true'" 49 | end 50 | 51 | it "doesn't allow 'url' to be missing if 'nothing' is not true" do 52 | expect { 53 | Entities::Embed.new({}) 54 | }.to raise_error Entity::ValidationError, "Either 'url' must be set or 'nothing' must be 'true'" 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/event_participation_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::EventParticipation do 5 | let(:parent) { Fabricate(:event, author: bob) } 6 | let(:parent_entity) { Fabricate(:related_entity, author: bob.diaspora_id) } 7 | let(:data) { 8 | Fabricate.attributes_for( 9 | :event_participation_entity, 10 | author: alice.diaspora_id, 11 | parent_guid: parent.guid, 12 | parent: parent_entity 13 | ).tap {|hash| add_signatures(hash) } 14 | } 15 | 16 | let(:xml) { <<~XML } 17 | 18 | #{data[:author]} 19 | #{data[:guid]} 20 | #{parent.guid} 21 | #{data[:status]} 22 | #{data[:edited_at].utc.iso8601} 23 | #{data[:author_signature]} 24 | 25 | XML 26 | 27 | let(:string) { "EventParticipation:#{data[:guid]}:#{parent.guid}" } 28 | 29 | it_behaves_like "an Entity subclass" 30 | 31 | it_behaves_like "an XML Entity" 32 | 33 | it_behaves_like "a relayable Entity" 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/event_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Event do 5 | let(:location) { Fabricate(:location_entity) } 6 | let(:data) { 7 | Fabricate.attributes_for(:event_entity).merge(author: alice.diaspora_id, location: location) 8 | } 9 | 10 | let(:xml) { <<~XML } 11 | 12 | #{data[:author]} 13 | #{data[:guid]} 14 | #{data[:edited_at].utc.iso8601} 15 | #{data[:summary]} 16 | #{data[:description]} 17 | #{data[:start].utc.iso8601} 18 | #{data[:end].utc.iso8601} 19 | #{data[:all_day]} 20 | #{data[:timezone]} 21 | 22 |
#{location.address}
23 | #{location.lat} 24 | #{location.lng} 25 |
26 |
27 | XML 28 | 29 | let(:string) { "Event:#{data[:guid]}" } 30 | 31 | it_behaves_like "an Entity subclass" 32 | 33 | it_behaves_like "an XML Entity" 34 | 35 | context "default values" do 36 | it "uses default values" do 37 | minimal_xml = <<~XML 38 | 39 | #{data[:author]} 40 | #{data[:guid]} 41 | #{data[:summary]} 42 | #{data[:start].utc.iso8601} 43 | 44 | XML 45 | 46 | parsed_xml = Nokogiri::XML(minimal_xml).root 47 | parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml) 48 | expect(parsed_instance.end).to be_nil 49 | expect(parsed_instance.all_day).to be_falsey 50 | expect(parsed_instance.timezone).to be_nil 51 | expect(parsed_instance.description).to be_nil 52 | expect(parsed_instance.location).to be_nil 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/location_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Location do 5 | let(:data) { Fabricate.attributes_for(:location_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 |
#{data[:address]}
10 | #{data[:lat]} 11 | #{data[:lng]} 12 |
13 | XML 14 | 15 | let(:json) { <<~JSON } 16 | { 17 | "entity_type": "location", 18 | "entity_data": { 19 | "address": "#{data[:address]}", 20 | "lat": "#{data[:lat]}", 21 | "lng": "#{data[:lng]}" 22 | } 23 | } 24 | JSON 25 | 26 | let(:string) { "Location" } 27 | 28 | it_behaves_like "an Entity subclass" 29 | 30 | it_behaves_like "an XML Entity" 31 | 32 | it_behaves_like "a JSON Entity" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/message_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Message do 5 | let(:data) { Fabricate.attributes_for(:message_entity, author: alice.diaspora_id) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:author]} 10 | #{data[:guid]} 11 | #{data[:text]} 12 | #{data[:created_at].utc.iso8601} 13 | #{data[:edited_at].utc.iso8601} 14 | #{data[:conversation_guid]} 15 | 16 | XML 17 | 18 | let(:string) { "Message:#{data[:guid]}:#{data[:conversation_guid]}" } 19 | 20 | it_behaves_like "an Entity subclass" 21 | 22 | it_behaves_like "an XML Entity" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/person_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Person do 5 | let(:data) { Fabricate.attributes_for(:person_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:guid]} 10 | #{data[:author]} 11 | #{data[:url]} 12 | 13 | #{data[:profile].author} 14 | #{data[:profile].edited_at.utc.iso8601} 15 | #{data[:profile].full_name} 16 | #{data[:profile].first_name} 17 | #{data[:profile].image_url} 18 | #{data[:profile].image_url} 19 | #{data[:profile].image_url} 20 | #{data[:profile].bio} 21 | #{data[:profile].birthday} 22 | #{data[:profile].gender} 23 | #{data[:profile].location} 24 | #{data[:profile].searchable} 25 | #{data[:profile].public} 26 | #{data[:profile].nsfw} 27 | #{data[:profile].tag_string} 28 | 29 | #{data[:exported_key]} 30 | 31 | XML 32 | 33 | let(:string) { "Person:#{data[:guid]}" } 34 | 35 | it_behaves_like "an Entity subclass" 36 | 37 | it_behaves_like "an XML Entity" 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/poll_answer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::PollAnswer do 5 | let(:data) { Fabricate.attributes_for(:poll_answer_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:guid]} 10 | #{data[:answer]} 11 | 12 | XML 13 | 14 | let(:json) { <<~JSON } 15 | { 16 | "entity_type": "poll_answer", 17 | "entity_data": { 18 | "guid": "#{data[:guid]}", 19 | "answer": "#{data[:answer]}" 20 | } 21 | } 22 | JSON 23 | 24 | let(:string) { "PollAnswer:#{data[:guid]}" } 25 | 26 | it_behaves_like "an Entity subclass" 27 | 28 | it_behaves_like "an XML Entity" 29 | 30 | it_behaves_like "a JSON Entity" 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/poll_participation_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::PollParticipation do 5 | let(:parent) { Fabricate(:poll, author: bob) } 6 | let(:parent_entity) { Fabricate(:related_entity, author: bob.diaspora_id) } 7 | let(:data) { 8 | Fabricate.attributes_for( 9 | :poll_participation_entity, 10 | author: alice.diaspora_id, 11 | parent_guid: parent.guid, 12 | parent: parent_entity 13 | ).tap {|hash| add_signatures(hash) } 14 | } 15 | 16 | let(:xml) { <<~XML } 17 | 18 | #{data[:author]} 19 | #{data[:guid]} 20 | #{parent.guid} 21 | #{data[:poll_answer_guid]} 22 | #{data[:author_signature]} 23 | 24 | XML 25 | 26 | let(:json) { <<~JSON } 27 | { 28 | "entity_type": "poll_participation", 29 | "entity_data": { 30 | "author": "#{data[:author]}", 31 | "guid": "#{data[:guid]}", 32 | "parent_guid": "#{parent.guid}", 33 | "author_signature": "#{data[:author_signature]}", 34 | "poll_answer_guid": "#{data[:poll_answer_guid]}" 35 | }, 36 | "property_order": [ 37 | "author", 38 | "guid", 39 | "parent_guid", 40 | "poll_answer_guid" 41 | ] 42 | } 43 | JSON 44 | 45 | let(:string) { "PollParticipation:#{data[:guid]}:#{parent.guid}" } 46 | 47 | it_behaves_like "an Entity subclass" 48 | 49 | it_behaves_like "an XML Entity" 50 | 51 | it_behaves_like "a JSON Entity" 52 | 53 | it_behaves_like "a relayable Entity" 54 | 55 | it_behaves_like "a relayable JSON entity" 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/poll_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Poll do 5 | let(:data) { Fabricate.attributes_for(:poll_entity) } 6 | 7 | let(:xml) { <<~XML } 8 | 9 | #{data[:guid]} 10 | #{data[:question]} 11 | #{data[:poll_answers].map {|a| indent(a.to_xml.to_s, 2) }.join("\n")} 12 | 13 | XML 14 | 15 | let(:json) { <<~JSON } 16 | { 17 | "entity_type": "poll", 18 | "entity_data": { 19 | "guid": "#{data[:guid]}", 20 | "question": "#{data[:question]}", 21 | "poll_answers": [ 22 | #{data[:poll_answers].map {|a| indent(JSON.pretty_generate(a.to_json), 6) }.join(",\n")} 23 | ] 24 | } 25 | } 26 | JSON 27 | 28 | let(:string) { "Poll:#{data[:guid]}" } 29 | 30 | it_behaves_like "an Entity subclass" 31 | 32 | it_behaves_like "an XML Entity" 33 | 34 | it_behaves_like "a JSON Entity" 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/entities/signable_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Entities::Signable do 5 | TEST_STRING_VALUE = "abc123" 6 | 7 | class TestSignableEntity < Entity 8 | include Entities::Signable 9 | 10 | property :my_signature, :string, default: nil 11 | 12 | def signature_data 13 | TEST_STRING_VALUE 14 | end 15 | end 16 | 17 | describe "#signature_data" do 18 | it "raises NotImplementedError when not overridden" do 19 | class TestEntity < Entity 20 | include Entities::Signable 21 | end 22 | 23 | expect { 24 | TestEntity.new({}).signature_data 25 | }.to raise_error(NotImplementedError) 26 | end 27 | end 28 | 29 | it_behaves_like "a signable" do 30 | let(:test_class) { TestSignableEntity } 31 | let(:test_string) { TEST_STRING_VALUE } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/parsers/base_parser_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Parsers::BaseParser do 5 | describe ".parse" do 6 | it "raises NotImplementedError error" do 7 | expect { 8 | Parsers::BaseParser.new(Entity).parse 9 | }.to raise_error(NotImplementedError, "you must override this method when creating your own parser") 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/parsers/relayable_json_parser_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Parsers::RelayableJsonParser do 5 | describe ".parse" do 6 | let(:entity_class) { Entities::SomeRelayable } 7 | let(:json_parser) { Parsers::RelayableJsonParser.new(entity_class) } 8 | include_examples ".parse parse error", 9 | "Required property is missing in JSON object: property_order", 10 | '{"entity_type": "some_relayable", "entity_data": {}}' 11 | 12 | it "returns property order as a second argument" do 13 | json = JSON.parse <<~JSON 14 | { 15 | "entity_type": "some_relayable", 16 | "property_order": ["property", "guid", "author"], 17 | "entity_data": { 18 | "author": "id@example.tld", 19 | "guid": "im a guid", 20 | "property": "value" 21 | } 22 | } 23 | JSON 24 | parsed_data = json_parser.parse(json) 25 | expect(parsed_data[0]).to be_a(Hash) 26 | expect(parsed_data[0][:guid]).to eq("im a guid") 27 | expect(parsed_data[0][:property]).to eq("value") 28 | expect(parsed_data[0][:author]).to eq("id@example.tld") 29 | expect(parsed_data[1]).to eq(%w[property guid author]) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/parsers/relayable_xml_parser_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Parsers::RelayableXmlParser do 5 | describe ".parse" do 6 | let(:entity_class) { Entities::SomeRelayable } 7 | let(:xml_parser) { Parsers::RelayableXmlParser.new(entity_class) } 8 | it "passes order of the XML elements as a second argument in the returned list" do 9 | xml_object = Nokogiri::XML(<<~XML).root 10 | 11 | im a guid 12 | value 13 | id@example.tld 14 | 15 | XML 16 | 17 | parsed_data = xml_parser.parse(xml_object) 18 | expect(parsed_data[0]).to be_a(Hash) 19 | expect(parsed_data[0][:guid]).to eq("im a guid") 20 | expect(parsed_data[0][:property]).to eq("value") 21 | expect(parsed_data[0][:author]).to eq("id@example.tld") 22 | expect(parsed_data[1]).to eq(%i[guid property author]) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/salmon/aes_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Salmon::AES do 5 | let(:data) { "test data string" } 6 | 7 | describe ".generate_key_and_iv" do 8 | it "generates a random key and iv" do 9 | key_and_iv = Salmon::AES.generate_key_and_iv 10 | 11 | expect(key_and_iv[:key]).not_to be_empty 12 | expect(key_and_iv[:iv]).not_to be_empty 13 | end 14 | 15 | it "generates a different key and iv every time" do 16 | key_and_iv = Salmon::AES.generate_key_and_iv 17 | key_and_iv2 = Salmon::AES.generate_key_and_iv 18 | 19 | expect(key_and_iv[:key]).not_to eq(key_and_iv2[:key]) 20 | expect(key_and_iv[:iv]).not_to eq(key_and_iv2[:iv]) 21 | end 22 | end 23 | 24 | describe ".encrypt" do 25 | let(:key_and_iv) { Salmon::AES.generate_key_and_iv } 26 | 27 | it "encrypts the data" do 28 | ciphertext = Salmon::AES.encrypt(data, key_and_iv[:key], key_and_iv[:iv]) 29 | 30 | expect(Base64.decode64(ciphertext)).not_to eq(data) 31 | end 32 | 33 | it "raises an error when the data is missing or the wrong type" do 34 | [nil, 1234, true, :symbol].each do |val| 35 | expect { 36 | Salmon::AES.encrypt(val, key_and_iv[:key], key_and_iv[:iv]) 37 | }.to raise_error ArgumentError 38 | end 39 | end 40 | end 41 | 42 | describe ".decrypt" do 43 | it "decrypts what it has encrypted" do 44 | key = Salmon::AES.generate_key_and_iv 45 | ciphertext = Salmon::AES.encrypt(data, key[:key], key[:iv]) 46 | 47 | decrypted_data = Salmon::AES.decrypt(ciphertext, key[:key], key[:iv]) 48 | 49 | expect(decrypted_data).to eq(data) 50 | end 51 | 52 | it "raises an error when the params are missing or the wrong type" do 53 | [nil, 1234, true, :symbol].each do |val| 54 | expect { 55 | Salmon::AES.decrypt(val, val, val) 56 | }.to raise_error ArgumentError 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/schemas_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "diaspora_federation/schemas" 4 | 5 | module DiasporaFederation 6 | describe Schemas do 7 | describe ".federation_entities" do 8 | it "returns the parsed federation_entities JSON schema" do 9 | schema = DiasporaFederation::Schemas.federation_entities 10 | expect(schema).to be_a(Hash) 11 | expect(schema["id"]).to eq(DiasporaFederation::Schemas::FEDERATION_ENTITIES_URI) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/account_deletion_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::AccountDeletionValidator do 5 | let(:entity) { :account_deletion_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/account_migration_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::AccountMigrationValidator do 5 | let(:entity) { :account_migration_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | describe "#profile" do 14 | it_behaves_like "a property with a value validation/restriction" do 15 | let(:property) { :profile } 16 | let(:wrong_values) { [nil] } 17 | let(:correct_values) { [] } 18 | end 19 | end 20 | 21 | describe "#old_identity" do 22 | it_behaves_like "a diaspora* ID validator" do 23 | let(:property) { :old_identity } 24 | end 25 | end 26 | 27 | describe "#remote_photo_path" do 28 | let(:property) { :remote_photo_path } 29 | 30 | it_behaves_like "a property with a value validation/restriction" do 31 | let(:wrong_values) { [] } 32 | let(:correct_values) { [nil] } 33 | end 34 | 35 | it_behaves_like "a url path validator" 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/comment_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::CommentValidator do 5 | let(:entity) { :comment_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a relayable validator" 10 | 11 | describe "#text" do 12 | it_behaves_like "a property with a value validation/restriction" do 13 | let(:property) { :text } 14 | let(:wrong_values) { ["", "a" * 65_536] } 15 | let(:correct_values) { ["a" * 65_535] } 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/contact_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::ContactValidator do 5 | let(:entity) { :contact_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | %i[author recipient].each do |prop| 10 | describe "##{prop}" do 11 | it_behaves_like "a diaspora* ID validator" do 12 | let(:property) { prop } 13 | end 14 | end 15 | end 16 | 17 | %i[following sharing blocking].each do |prop| 18 | describe "##{prop}" do 19 | it_behaves_like "a boolean validator" do 20 | let(:property) { prop } 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/conversation_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::ConversationValidator do 5 | let(:entity) { :conversation_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | it_behaves_like "a guid validator" do 14 | let(:property) { :guid } 15 | end 16 | 17 | describe "#subject" do 18 | it_behaves_like "a property with a value validation/restriction" do 19 | let(:property) { :subject } 20 | let(:wrong_values) { [nil, "", "a" * 256] } 21 | let(:correct_values) { ["a" * 255] } 22 | end 23 | end 24 | 25 | describe "#messages" do 26 | it_behaves_like "a property with a value validation/restriction" do 27 | let(:property) { :messages } 28 | let(:wrong_values) { [nil] } 29 | let(:correct_values) { [[], [Fabricate(:message_entity)]] } 30 | end 31 | end 32 | 33 | describe "#participant_ids" do 34 | it_behaves_like "a property with a value validation/restriction" do 35 | let(:property) { :participants } 36 | let(:wrong_values) { ["", "foo;bar", Fabricate.sequence(:diaspora_id)] } 37 | let(:correct_values) { 38 | [ 39 | Array.new(2) { Fabricate.sequence(:diaspora_id) }.join(";"), 40 | Array.new(21) { Fabricate.sequence(:diaspora_id) }.join(";") 41 | ] 42 | } 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/embed_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::EmbedValidator do 5 | let(:entity) { :embed_entity } 6 | it_behaves_like "a common validator" 7 | 8 | describe "#url" do 9 | it_behaves_like "a property with a value validation/restriction" do 10 | let(:property) { :url } 11 | let(:wrong_values) { %w[https://asdf$%.com example.com] } 12 | let(:correct_values) { [nil, "https://example.org", "https://example.org/index.html"] } 13 | end 14 | end 15 | 16 | describe "#title" do 17 | it_behaves_like "a length validator" do 18 | let(:property) { :title } 19 | let(:length) { 255 } 20 | end 21 | end 22 | 23 | describe "#description" do 24 | it_behaves_like "a length validator" do 25 | let(:property) { :description } 26 | let(:length) { 65_535 } 27 | end 28 | end 29 | 30 | describe "#image" do 31 | it_behaves_like "a property with a value validation/restriction" do 32 | let(:property) { :image } 33 | let(:wrong_values) { %w[https://asdf$%.com example.com] } 34 | let(:correct_values) { [nil] } 35 | end 36 | 37 | it_behaves_like "a url path validator" do 38 | let(:property) { :image } 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/event_participation_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::EventParticipationValidator do 5 | let(:entity) { :event_participation_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a relayable validator" 10 | 11 | describe "#status" do 12 | it_behaves_like "a property with a value validation/restriction" do 13 | let(:property) { :status } 14 | let(:wrong_values) { ["", "yes", "foobar"] } 15 | let(:correct_values) { %w[accepted declined tentative] } 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/event_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::EventValidator do 5 | let(:entity) { :event_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | it_behaves_like "a guid validator" do 14 | let(:property) { :guid } 15 | end 16 | 17 | describe "#summary" do 18 | it_behaves_like "a property with a value validation/restriction" do 19 | let(:property) { :summary } 20 | let(:wrong_values) { ["a" * 256, nil, ""] } 21 | let(:correct_values) { ["a" * 255] } 22 | end 23 | end 24 | 25 | describe "#description" do 26 | it_behaves_like "a property with a value validation/restriction" do 27 | let(:property) { :description } 28 | let(:wrong_values) { ["a" * 65_536] } 29 | let(:correct_values) { ["a" * 65_535, nil, ""] } 30 | end 31 | end 32 | 33 | describe "#start" do 34 | it_behaves_like "a property with a value validation/restriction" do 35 | let(:property) { :start } 36 | let(:wrong_values) { [nil] } 37 | let(:correct_values) { [Time.now.utc] } 38 | end 39 | end 40 | 41 | describe "#end" do 42 | it_behaves_like "a property with a value validation/restriction" do 43 | let(:property) { :end } 44 | let(:wrong_values) { [] } 45 | let(:correct_values) { [nil, Time.now.utc] } 46 | end 47 | end 48 | 49 | describe "#all_day" do 50 | it_behaves_like "a boolean validator" do 51 | let(:property) { :all_day } 52 | end 53 | end 54 | 55 | describe "#timezone" do 56 | it_behaves_like "a property with a value validation/restriction" do 57 | let(:property) { :timezone } 58 | let(:wrong_values) { ["foobar"] } 59 | let(:correct_values) { [nil, "Europe/Berlin", "America/Argentina/ComodRivadavia"] } 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/h_card_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::HCardValidator do 5 | let(:entity) { :h_card } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | describe "#full_name" do 10 | it_behaves_like "a name validator" do 11 | let(:property) { :full_name } 12 | let(:length) { 70 } 13 | end 14 | end 15 | 16 | %i[first_name last_name].each do |prop| 17 | describe "##{prop}" do 18 | it_behaves_like "a name validator" do 19 | let(:property) { prop } 20 | let(:length) { 32 } 21 | end 22 | end 23 | end 24 | 25 | %i[photo_large_url photo_medium_url photo_small_url].each do |prop| 26 | describe "##{prop}" do 27 | it_behaves_like "a property that mustn't be empty" do 28 | let(:property) { prop } 29 | end 30 | 31 | it_behaves_like "a url path validator" do 32 | let(:property) { prop } 33 | end 34 | end 35 | end 36 | 37 | describe "#searchable" do 38 | it_behaves_like "a boolean validator" do 39 | let(:property) { :searchable } 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/like_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::LikeValidator do 5 | let(:entity) { :like_entity } 6 | it_behaves_like "a common validator" 7 | 8 | it_behaves_like "a relayable validator" 9 | 10 | describe "#parent_type" do 11 | it_behaves_like "a property with a value validation/restriction" do 12 | let(:property) { :parent_type } 13 | let(:wrong_values) { [nil, "", "any", "Postxxx", "post"] } 14 | let(:correct_values) { %w[Post Comment] } 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/location_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::LocationValidator do 5 | let(:entity) { :location_entity } 6 | it_behaves_like "a common validator" 7 | 8 | %i[lat lng].each do |prop| 9 | describe "##{prop}" do 10 | it_behaves_like "a property that mustn't be empty" do 11 | let(:property) { prop } 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/message_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::MessageValidator do 5 | let(:entity) { :message_entity } 6 | it_behaves_like "a common validator" 7 | 8 | it_behaves_like "a diaspora* ID validator" do 9 | let(:property) { :author } 10 | end 11 | 12 | describe "#guid" do 13 | it_behaves_like "a guid validator" do 14 | let(:property) { :guid } 15 | end 16 | end 17 | 18 | describe "#conversation_guid" do 19 | it_behaves_like "a guid validator" do 20 | let(:property) { :conversation_guid } 21 | end 22 | end 23 | 24 | describe "#text" do 25 | it_behaves_like "a property with a value validation/restriction" do 26 | let(:property) { :text } 27 | let(:wrong_values) { ["", "a" * 65_536] } 28 | let(:correct_values) { ["a" * 65_535] } 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/participation_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::ParticipationValidator do 5 | let(:entity) { :participation_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | describe "#guid" do 14 | it_behaves_like "a guid validator" do 15 | let(:property) { :guid } 16 | end 17 | end 18 | 19 | describe "#parent_guid" do 20 | it_behaves_like "a guid validator" do 21 | let(:property) { :parent_guid } 22 | end 23 | end 24 | 25 | describe "#parent_type" do 26 | it_behaves_like "a property with a value validation/restriction" do 27 | let(:property) { :parent_type } 28 | let(:wrong_values) { [nil, "", "any", "Postxxx", "post"] } 29 | let(:correct_values) { ["Post"] } 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/person_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::PersonValidator do 5 | let(:entity) { :person_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | it_behaves_like "a guid validator" do 14 | let(:property) { :guid } 15 | end 16 | 17 | describe "#url" do 18 | it_behaves_like "a url validator without path" do 19 | let(:property) { :url } 20 | end 21 | end 22 | 23 | describe "#profile" do 24 | it_behaves_like "a property with a value validation/restriction" do 25 | let(:property) { :profile } 26 | let(:wrong_values) { [nil] } 27 | let(:correct_values) { [] } 28 | end 29 | end 30 | 31 | it_behaves_like "a public key validator" do 32 | let(:property) { :exported_key } 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/photo_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::PhotoValidator do 5 | let(:entity) { :photo_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | describe "#guid" do 14 | it_behaves_like "a guid validator" do 15 | let(:property) { :guid } 16 | end 17 | end 18 | 19 | describe "#status_message_guid" do 20 | it_behaves_like "a nilable guid validator" do 21 | let(:property) { :status_message_guid } 22 | end 23 | end 24 | 25 | it_behaves_like "a boolean validator" do 26 | let(:property) { :public } 27 | end 28 | 29 | describe "#remote_photo_path" do 30 | let(:property) { :remote_photo_path } 31 | 32 | it_behaves_like "a property that mustn't be empty" 33 | 34 | it_behaves_like "a url path validator" 35 | end 36 | 37 | describe "#remote_photo_name" do 38 | it_behaves_like "a property that mustn't be empty" do 39 | let(:property) { :remote_photo_name } 40 | end 41 | end 42 | 43 | %i[height width].each do |prop| 44 | describe "##{prop}" do 45 | it_behaves_like "a property with a value validation/restriction" do 46 | let(:property) { prop } 47 | let(:wrong_values) { [true, :num, "asdf"] } 48 | let(:correct_values) { [123, "123", nil] } 49 | end 50 | end 51 | end 52 | 53 | describe "#text" do 54 | it_behaves_like "a property with a value validation/restriction" do 55 | let(:property) { :text } 56 | let(:wrong_values) { ["a" * 65_536] } 57 | let(:correct_values) { ["a" * 65_535, nil, ""] } 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/poll_answer_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::PollAnswerValidator do 5 | let(:entity) { :poll_answer_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | describe "#guid" do 10 | it_behaves_like "a guid validator" do 11 | let(:property) { :guid } 12 | end 13 | end 14 | 15 | describe "#answer" do 16 | it_behaves_like "a property with a value validation/restriction" do 17 | let(:property) { :answer } 18 | let(:wrong_values) { [nil, "", "a" * 256] } 19 | let(:correct_values) { ["a" * 255] } 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/poll_participation_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::PollParticipationValidator do 5 | let(:entity) { :poll_participation_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a relayable validator" 10 | 11 | describe "#poll_answer_guid" do 12 | it_behaves_like "a guid validator" do 13 | let(:property) { :poll_answer_guid } 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/poll_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::PollValidator do 5 | let(:entity) { :poll_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | describe "#guid" do 10 | it_behaves_like "a guid validator" do 11 | let(:property) { :guid } 12 | end 13 | end 14 | 15 | describe "#question" do 16 | it_behaves_like "a property with a value validation/restriction" do 17 | let(:property) { :question } 18 | let(:wrong_values) { [nil, "", "a" * 256] } 19 | let(:correct_values) { ["a" * 255] } 20 | end 21 | end 22 | 23 | describe "#poll_answers" do 24 | it_behaves_like "a property with a value validation/restriction" do 25 | let(:property) { :poll_answers } 26 | let(:wrong_values) { [nil, [Fabricate.attributes_for(:poll_answer_entity)]] } 27 | let(:correct_values) { 28 | [ 29 | Array.new(2) { Fabricate(:poll_answer_entity) }, 30 | Array.new(5) { Fabricate(:poll_answer_entity) } 31 | ] 32 | } 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/related_entity_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::RelatedEntityValidator do 5 | let(:entity) { :related_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | %i[local public].each do |prop| 14 | it_behaves_like "a boolean validator" do 15 | let(:property) { prop } 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/reshare_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::ReshareValidator do 5 | let(:entity) { :reshare_entity } 6 | it_behaves_like "a common validator" 7 | 8 | describe "#author" do 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | end 13 | 14 | describe "#guid" do 15 | it_behaves_like "a guid validator" do 16 | let(:property) { :guid } 17 | end 18 | end 19 | 20 | describe "#root_guid" do 21 | it_behaves_like "a nilable guid validator" do 22 | let(:property) { :root_guid } 23 | end 24 | end 25 | 26 | describe "#root_author" do 27 | it_behaves_like "a property with a value validation/restriction" do 28 | let(:property) { :root_author } 29 | let(:wrong_values) { ["i am a weird diaspora* ID @@@ ### 12345"] } 30 | let(:correct_values) { [nil, "alice@example.org"] } 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/retraction_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::RetractionValidator do 5 | let(:entity) { :retraction_entity } 6 | it_behaves_like "a common validator" 7 | 8 | it_behaves_like "a guid validator" do 9 | let(:property) { :target_guid } 10 | end 11 | 12 | it_behaves_like "a diaspora* ID validator" do 13 | let(:property) { :author } 14 | end 15 | 16 | describe "#target_type" do 17 | it_behaves_like "a property that mustn't be empty" do 18 | let(:property) { :target_type } 19 | end 20 | end 21 | 22 | describe "#target" do 23 | it_behaves_like "a property with a value validation/restriction" do 24 | let(:property) { :target } 25 | let(:wrong_values) { [nil] } 26 | let(:correct_values) { [Fabricate(:related_entity)] } 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/rules/birthday_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe Validation::Rule::Birthday do 4 | it "will not accept parameters" do 5 | validator = Validation::Validator.new({}) 6 | expect { 7 | validator.rule(:birthday, birthday: {param: true}) 8 | }.to raise_error ArgumentError 9 | end 10 | 11 | it "has an error key" do 12 | expect(described_class.new.error_key).to eq(:birthday) 13 | end 14 | 15 | context "when validating" do 16 | before do 17 | stub_const("BirthdayHolder", Struct.new(:birthday)) 18 | end 19 | 20 | it "validates a date object" do 21 | validator = Validation::Validator.new(BirthdayHolder.new(Date.new)) 22 | validator.rule(:birthday, :birthday) 23 | 24 | expect(validator).to be_valid 25 | expect(validator.errors).to be_empty 26 | end 27 | 28 | it "validates a string" do 29 | validator = Validation::Validator.new(BirthdayHolder.new("2015-07-19")) 30 | validator.rule(:birthday, :birthday) 31 | 32 | expect(validator).to be_valid 33 | expect(validator.errors).to be_empty 34 | end 35 | 36 | it "allows nil and empty" do 37 | [nil, ""].each do |val| 38 | validator = Validation::Validator.new(BirthdayHolder.new(val)) 39 | validator.rule(:birthday, :birthday) 40 | 41 | expect(validator).to be_valid 42 | expect(validator.errors).to be_empty 43 | end 44 | end 45 | 46 | it "fails for invalid date string" do 47 | validator = Validation::Validator.new(BirthdayHolder.new("i'm no date")) 48 | validator.rule(:birthday, :birthday) 49 | 50 | expect(validator).not_to be_valid 51 | expect(validator.errors).to include(:birthday) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/rules/not_nil_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe Validation::Rule::NotNil do 4 | it "will not accept parameters" do 5 | validator = Validation::Validator.new({}) 6 | expect { 7 | validator.rule(:not_nil, not_nil: {param: true}) 8 | }.to raise_error ArgumentError 9 | end 10 | 11 | it "has an error key" do 12 | expect(described_class.new.error_key).to eq(:not_nil) 13 | end 14 | 15 | context "when validating" do 16 | before do 17 | stub_const("ValueHolder", Struct.new(:value)) 18 | end 19 | 20 | it "validates a string" do 21 | validator = Validation::Validator.new(ValueHolder.new("abcd")) 22 | validator.rule(:value, :not_nil) 23 | 24 | expect(validator).to be_valid 25 | expect(validator.errors).to be_empty 26 | end 27 | 28 | it "validates a object" do 29 | validator = Validation::Validator.new(ValueHolder.new(Object.new)) 30 | validator.rule(:value, :not_nil) 31 | 32 | expect(validator).to be_valid 33 | expect(validator.errors).to be_empty 34 | end 35 | 36 | it "fails if it is nil" do 37 | validator = Validation::Validator.new(ValueHolder.new(nil)) 38 | validator.rule(:value, :not_nil) 39 | 40 | expect(validator).not_to be_valid 41 | expect(validator.errors).to include(:value) 42 | end 43 | 44 | it "allows an empty string" do 45 | validator = Validation::Validator.new(ValueHolder.new("")) 46 | validator.rule(:value, :not_nil) 47 | 48 | expect(validator).to be_valid 49 | expect(validator.errors).to be_empty 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/status_message_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::StatusMessageValidator do 5 | let(:entity) { :status_message_entity } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | it_behaves_like "a diaspora* ID validator" do 10 | let(:property) { :author } 11 | end 12 | 13 | it_behaves_like "a guid validator" do 14 | let(:property) { :guid } 15 | end 16 | 17 | describe "#photos" do 18 | it_behaves_like "a property with a value validation/restriction" do 19 | let(:property) { :photos } 20 | let(:wrong_values) { [nil] } 21 | let(:correct_values) { [[], [Fabricate(:photo_entity)]] } 22 | end 23 | end 24 | 25 | it_behaves_like "a boolean validator" do 26 | let(:property) { :public } 27 | end 28 | 29 | describe "#text" do 30 | it_behaves_like "a property with a value validation/restriction" do 31 | let(:property) { :text } 32 | let(:wrong_values) { ["a" * 65_536] } 33 | let(:correct_values) { ["a" * 65_535, nil, ""] } 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/lib/diaspora_federation/validators/web_finger_validator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe Validators::WebFingerValidator do 5 | let(:entity) { :webfinger } 6 | 7 | it_behaves_like "a common validator" 8 | 9 | describe "#acct_uri" do 10 | it_behaves_like "a property that mustn't be empty" do 11 | let(:property) { :acct_uri } 12 | end 13 | end 14 | 15 | %i[hcard_url].each do |prop| 16 | describe "##{prop}" do 17 | it_behaves_like "a url validator without path" do 18 | let(:property) { prop } 19 | end 20 | 21 | it_behaves_like "a url path validator" do 22 | let(:property) { prop } 23 | end 24 | end 25 | end 26 | 27 | # optional urls 28 | %i[salmon_url profile_url atom_url].each do |prop| 29 | describe "##{prop}" do 30 | it_behaves_like "a property with a value validation/restriction" do 31 | let(:property) { prop } 32 | let(:wrong_values) { %w[https://asdf$%.com example.com] } 33 | let(:correct_values) { [nil] } 34 | end 35 | 36 | it_behaves_like "a url path validator" do 37 | let(:property) { prop } 38 | end 39 | end 40 | end 41 | 42 | describe "#seed_url" do 43 | it_behaves_like "a url validator without path" do 44 | let(:property) { :seed_url } 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/routing/fetch_routing_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe ReceiveController, type: :routing do 5 | routes { DiasporaFederation::Engine.routes } 6 | 7 | let(:guid) { "12345678901234567890abcdefgh" } 8 | 9 | it "routes post fetch" do 10 | expect(get: "/fetch/post/#{guid}").to route_to( 11 | controller: "diaspora_federation/fetch", 12 | action: "fetch", 13 | type: "post", 14 | guid: guid 15 | ) 16 | end 17 | 18 | it "routes post fetch" do 19 | expect(get: "/fetch/status_message/#{guid}").to route_to( 20 | controller: "diaspora_federation/fetch", 21 | action: "fetch", 22 | type: "status_message", 23 | guid: guid 24 | ) 25 | end 26 | 27 | it "routes post fetch with GUID with dots (hubzilla)" do 28 | guid = "1234567890abcd@hubzilla.example.org" 29 | expect(get: "/fetch/post/#{guid}").to route_to( 30 | controller: "diaspora_federation/fetch", 31 | action: "fetch", 32 | type: "post", 33 | guid: guid 34 | ) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/routing/receive_routing_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe ReceiveController, type: :routing do 5 | routes { DiasporaFederation::Engine.routes } 6 | 7 | it "routes POST public" do 8 | expect(post: "/receive/public").to route_to( 9 | controller: "diaspora_federation/receive", 10 | action: "public" 11 | ) 12 | end 13 | 14 | it "routes POST private" do 15 | expect(post: "/receive/users/1234").to route_to( 16 | controller: "diaspora_federation/receive", 17 | action: "private", 18 | guid: "1234" 19 | ) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/routing/webfinger_routing_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DiasporaFederation 4 | describe ReceiveController, type: :routing do 5 | routes { DiasporaFederation::Engine.routes } 6 | 7 | it "routes GET webfinger" do 8 | expect(get: "/.well-known/webfinger").to route_to( 9 | controller: "diaspora_federation/webfinger", 10 | action: "webfinger" 11 | ) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/fixture_builder.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # set default users as initial database for each test 4 | RSpec.configure do |config| 5 | config.before(:suite) do 6 | Person.reset_database 7 | Fabricate(:user, diaspora_id: "alice@localhost:3000") 8 | Fabricate(:user, diaspora_id: "bob@localhost:3000") 9 | Person.init_database = Person.database 10 | end 11 | 12 | config.after(:each) do 13 | Entity.reset_database 14 | Person.reset_database 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/support/helper_methods.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # default users 4 | def alice 5 | @alice ||= Person.find_by(diaspora_id: "alice@localhost:3000") 6 | end 7 | 8 | def bob 9 | @bob ||= Person.find_by(diaspora_id: "bob@localhost:3000") 10 | end 11 | 12 | # callback expectation helper 13 | def expect_callback(*opts) 14 | expect(DiasporaFederation.callbacks).to receive(:trigger).with(*opts) 15 | end 16 | 17 | # signature methods 18 | def add_signatures(hash, klass=described_class) 19 | properties = klass.new(hash).send(:xml_elements) 20 | hash[:author_signature] = properties[:author_signature] 21 | end 22 | 23 | def sign_with_key(privkey, signature_data) 24 | Base64.strict_encode64(privkey.sign(OpenSSL::Digest.new("SHA256"), signature_data)) 25 | end 26 | 27 | def verify_signature(pubkey, signature, signed_string) 28 | pubkey.verify(OpenSSL::Digest.new("SHA256"), Base64.decode64(signature), signed_string) 29 | end 30 | 31 | # time helper 32 | def change_time(time, options={}) 33 | new_hour = options.fetch(:hour, time.hour) 34 | new_min = options.fetch(:min, options[:hour] ? 0 : time.min) 35 | new_sec = options.fetch(:sec, options[:hour] || options[:min] ? 0 : time.sec) 36 | 37 | Time.utc(time.year, time.month, time.day, new_hour, new_min, new_sec) 38 | end 39 | 40 | # indent helper 41 | def indent(string, amount) 42 | string.gsub(/^/, " " * amount) 43 | end 44 | -------------------------------------------------------------------------------- /spec/support/shared_magic_envelope_specs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | shared_examples "a MagicEnvelope instance" do 4 | before do 5 | allow(DiasporaFederation.callbacks).to receive(:trigger).with( 6 | :fetch_public_key, sender 7 | ).and_return(privkey.public_key) 8 | end 9 | 10 | it "is an instance of MagicEnvelope" do 11 | expect(subject).to be_an_instance_of DiasporaFederation::Salmon::MagicEnvelope 12 | end 13 | 14 | it "should match the sender" do 15 | expect(subject.sender).to eq(sender) 16 | end 17 | 18 | it "returns the entity" do 19 | entity = subject.payload 20 | expect(entity).to be_an_instance_of DiasporaFederation::Entities::TestEntity 21 | expect(entity.test).to eq(payload.test) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/shared_parser_specs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | shared_examples ".parse parse error" do |reason, json| 4 | it "raises error when #{reason}" do 5 | expect { 6 | json_parser.parse(JSON.parse(json)) 7 | }.to raise_error DiasporaFederation::Parsers::JsonParser::DeserializationError, reason 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This is a dummy rails application to debug the diaspora federation gem. -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 5 | 6 | require File.expand_path("config/application", __dir__) 7 | 8 | Rails.application.load_tasks 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/entity.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Entity 4 | attr_accessor :author, :guid 5 | attr_reader :entity_type 6 | 7 | def initialize(entity_type) 8 | @entity_type = entity_type 9 | @guid = UUID.generate(:compact) 10 | end 11 | 12 | def save! 13 | Entity.database[entity_type][guid] = self 14 | end 15 | 16 | class << self 17 | def find_by(opts) 18 | database[opts[:entity_type]][opts[:guid]] 19 | end 20 | 21 | def database 22 | @database ||= Hash.new({}) 23 | end 24 | 25 | def reset_database 26 | @database = nil 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/dummy/app/models/person.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Person 4 | attr_accessor :diaspora_id, :url, :guid, :serialized_public_key, :serialized_private_key 5 | 6 | def initialize 7 | @guid = UUID.generate(:compact) 8 | end 9 | 10 | def private_key; OpenSSL::PKey::RSA.new(serialized_private_key) end 11 | def public_key; OpenSSL::PKey::RSA.new(serialized_public_key) end 12 | 13 | def alias_url; "#{url}people/#{guid}" end 14 | def hcard_url; "#{url}hcard/users/#{guid}" end 15 | def profile_url; "#{url}u/#{nickname}" end 16 | def atom_url; "#{url}public/#{nickname}.atom" end 17 | def salmon_url; "#{url}receive/users/#{guid}" end 18 | def subscribe_url; "#{url}people?q={uri}" end 19 | 20 | def nickname; diaspora_id.split("@")[0] end 21 | 22 | def photo_default_url; "#{url}assets/user/default.png" end 23 | 24 | def searchable; true end 25 | def full_name; "Dummy User" end 26 | def first_name; "Dummy" end 27 | def last_name; "User" end 28 | 29 | def save! 30 | Person.database[:diaspora_id][diaspora_id] = self 31 | Person.database[:guid][guid] = self 32 | end 33 | 34 | class << self 35 | attr_writer :init_database 36 | 37 | def find_by(opts) 38 | return database[:diaspora_id][opts[:diaspora_id]] if opts[:diaspora_id] 39 | 40 | database[:guid][opts[:guid]] 41 | end 42 | 43 | def database 44 | @database ||= @init_database || {diaspora_id: {}, guid: {}} 45 | end 46 | 47 | def reset_database 48 | @database = nil 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | puts "\n== Removing old logs and tempfiles ==" 21 | system! "bin/rails log:clear tmp:clear" 22 | 23 | puts "\n== Restarting application server ==" 24 | system! "bin/rails restart" 25 | end 26 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require_relative "config/environment" 6 | 7 | run Rails.application 8 | Rails.application.load_server 9 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "boot" 4 | 5 | require "rails" 6 | # Pick the frameworks you want: 7 | # require "active_model/railtie" 8 | # require "active_job/railtie" 9 | # require "active_record/railtie" 10 | # require "active_storage/engine" 11 | require "action_controller/railtie" 12 | # require "action_mailer/railtie" 13 | # require "action_mailbox/engine" 14 | # require "action_text/engine" 15 | require "action_view/railtie" 16 | # require "action_cable/engine" 17 | require "rails/test_unit/railtie" 18 | 19 | # Require the gems listed in Gemfile, including any gems 20 | # you've limited to :test, :development, or :production. 21 | Bundler.require(*Rails.groups) 22 | require "diaspora_federation/rails" 23 | 24 | module Dummy 25 | class Application < Rails::Application 26 | # Initialize configuration defaults for originally generated Rails version. 27 | config.load_defaults(ENV["RAILS_VERSION"] || "7.0") 28 | 29 | # Configuration for the application, engines, and railties goes here. 30 | # 31 | # These settings can be overridden in specific environments using the files 32 | # in config/environments, which are processed later. 33 | # 34 | # config.time_zone = "Central Time (US & Canada)" 35 | # config.eager_load_paths << Rails.root.join("extras") 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) 4 | 5 | require "bundler/setup" # Set up gems listed in the Gemfile. 6 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative "application" 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support/core_ext/integer/time" 4 | 5 | Rails.application.configure do 6 | # Settings specified here will take precedence over those in config/application.rb. 7 | 8 | # In the development environment your application's code is reloaded any time 9 | # it changes. This slows down response time but is perfect for development 10 | # since you don't have to restart the web server when you make code changes. 11 | config.cache_classes = false 12 | 13 | # Do not eager load code on boot. 14 | config.eager_load = false 15 | 16 | # Show full error reports. 17 | config.consider_all_requests_local = true 18 | 19 | # Enable server timing 20 | config.server_timing = true 21 | 22 | # Enable/disable caching. By default caching is disabled. 23 | # Run rails dev:cache to toggle caching. 24 | if Rails.root.join("tmp/caching-dev.txt").exist? 25 | config.action_controller.perform_caching = true 26 | config.action_controller.enable_fragment_cache_logging = true 27 | 28 | config.cache_store = :memory_store 29 | config.public_file_server.headers = { 30 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 31 | } 32 | else 33 | config.action_controller.perform_caching = false 34 | 35 | config.cache_store = :null_store 36 | end 37 | 38 | # Print deprecation notices to the Rails logger. 39 | config.active_support.deprecation = :log 40 | 41 | # Raise exceptions for disallowed deprecations. 42 | config.active_support.disallowed_deprecation = :raise 43 | 44 | # Tell Active Support which deprecation messages to disallow. 45 | config.active_support.disallowed_deprecation_warnings = [] 46 | 47 | # Raises error for missing translations. 48 | # config.i18n.raise_on_missing_translations = true 49 | 50 | # Annotate rendered view with file names. 51 | # config.action_view.annotate_rendered_view_with_filenames = true 52 | 53 | # Uncomment if you wish to allow Action Cable access from any origin. 54 | # config.action_cable.disable_request_forgery_protection = true 55 | end 56 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 6 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } 7 | 8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code 9 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". 10 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Specify a serializer for the signed and encrypted cookie jars. 6 | # Valid options are :json, :marshal, and :hybrid. 7 | Rails.application.config.action_dispatch.cookies_serializer = :marshal 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of 6 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported 7 | # notations and behaviors. 8 | Rails.application.config.filter_parameters += %i[ 9 | passw secret token _key crypt salt certificate otp ssn 10 | ] 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | Rails.application.config.session_store :cookie_store, key: "_dummy_session" 6 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.routes.draw do 4 | # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html 5 | 6 | mount DiasporaFederation::Engine => "/" 7 | 8 | get "discovery" => "discovery#discovery" 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 944b2cfb9fc44368fc73cdd7f5f09349f6a064c582f6352a179dfcb4d99cb62035304b3603b1fa3deb79d1de24c73ef1d8006e60e2a1cff2266b9ecca908a8f0 15 | 16 | test: 17 | secret_key_base: b5cc0bf00de1626234552283a86b3e6d629491ad64c2b51bc39ae57743aca373e9854b278650555ab14a1581223929798197ca8c069849528eafaff5caa48416 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diaspora/diaspora_federation/4468b8b8e111aaf4924e7f1431c8359788b116cf/test/dummy/log/.keep -------------------------------------------------------------------------------- /test/scripts/ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | if [[ ${BUNDLE_GEMFILE} =~ .*test/gemfiles/.*.Gemfile ]]; then 6 | if [[ ${BUNDLE_GEMFILE} =~ .*/no-rails.Gemfile ]]; then 7 | if grep activesupport "${BUNDLE_GEMFILE}.lock"; then 8 | echo "ERROR! no-rails.Gemfile.lock contains rails dependency!" 9 | exit 1 10 | fi 11 | fi 12 | 13 | # No coverage for other gemfiles, because some specs are disabled 14 | export NO_COVERAGE="true" 15 | bundle exec rake --trace 16 | else 17 | if [[ -n ${CC_TEST_REPORTER_ID} ]]; then 18 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 19 | chmod +x ./cc-test-reporter 20 | ./cc-test-reporter before-build 21 | fi 22 | 23 | bundle exec rake --trace 24 | test_exit_code=$? 25 | 26 | if [[ -n ${CC_TEST_REPORTER_ID} ]]; then 27 | ./cc-test-reporter after-build --exit-code ${test_exit_code} 28 | fi 29 | 30 | exit ${test_exit_code} 31 | fi 32 | --------------------------------------------------------------------------------