Add links plugin

This can be used to link to external services related to the foodcoop.
With the indirect mode it is possible to implement a secure login to other
services. In that case Foodsoft will send a HTTP GET request and redirect
the user to the returned Location header. This allows the generation of
a one-time login URL.
A typical use-case would be that a workgroup, which is responsible for
the email account, does not need to share the login credentials and can
use a link within the Foodsoft instead.
This commit is contained in:
Patrick Gansterer 2020-07-29 11:19:20 +02:00
parent e16f03eebf
commit 7657b05787
16 changed files with 211 additions and 0 deletions

View File

@ -61,6 +61,7 @@ gem 'foodsoft_wiki', path: 'plugins/wiki'
gem 'foodsoft_messages', path: 'plugins/messages'
gem 'foodsoft_documents', path: 'plugins/documents'
gem 'foodsoft_discourse', path: 'plugins/discourse'
gem 'foodsoft_links', path: 'plugins/links'
gem 'foodsoft_polls', path: 'plugins/polls'
# plugins not enabled by default

View File

@ -26,6 +26,13 @@ PATH
rails
ruby-filemagic
PATH
remote: plugins/links
specs:
foodsoft_links (0.0.1)
deface (~> 1.0)
rails
PATH
remote: plugins/messages
specs:
@ -534,6 +541,7 @@ DEPENDENCIES
faker
foodsoft_discourse!
foodsoft_documents!
foodsoft_links!
foodsoft_messages!
foodsoft_polls!
foodsoft_wiki!

View File

@ -0,0 +1,11 @@
class CreateLinks < ActiveRecord::Migration
def change
create_table :links do |t|
t.string :name, null: false
t.string :url, null: false
t.references :workgroup
t.boolean :indirect, null: false, default: false
t.string :authorization
end
end
end

View File

@ -235,6 +235,14 @@ ActiveRecord::Schema.define(version: 20181205010000) do
add_index "invoices", ["supplier_id"], name: "index_invoices_on_supplier_id", using: :btree
create_table "links", force: :cascade do |t|
t.string "name", null: false
t.string "url", null: false
t.integer "workgroup_id"
t.boolean "indirect", default: false, null: false
t.string "authorization"
end
create_table "mail_delivery_status", force: :cascade do |t|
t.datetime "created_at"
t.string "email", limit: 255, null: false

25
plugins/links/README.md Normal file
View File

@ -0,0 +1,25 @@
FoodsoftLinks
=================
This plugin adds links to foodsoft. A new 'Links' menu entry is added in the
navigation bar, if there are visible links for the user.
This plugin is enabled by default in foodsoft, so you don't need to do anything
to install it. If you still want to, for example when it has been disabled,
add the following to foodsoft's Gemfile:
```Gemfile
gem 'foodsoft_links', path: 'lib/foodsoft_links'
```
It can be used to link to external services related to the foodcoop.
With the indirect mode it is possible to implement a secure login to other
services. In that case Foodsoft will send a HTTP GET request and redirect
the user to the returned Location header. This allows the generation of
a one-time login URL.
A typical use-case would be that a workgroup, which is responsible for
the email account, does not need to share the login credentials and can
use a link within the Foodsoft instead.
This plugin is part of the foodsoft package and uses the AGPL-3 license (see
foodsoft's LICENSE for the full license text).

40
plugins/links/Rakefile Normal file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env rake
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
begin
require 'rdoc/task'
rescue LoadError
require 'rdoc/rdoc'
require 'rake/rdoctask'
RDoc::Task = Rake::RDocTask
end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'FoodsoftLinks'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
Bundler::GemHelper.install_tasks
require 'rake/testtask'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test

View File

@ -0,0 +1,29 @@
require 'net/http'
class LinksController < ApplicationController
def show
link = Link.find(params[:id])
url = link.url
if link.workgroup && !current_user.role_admin? && !link.workgroup.member?(current_user)
return deny_access
end
if link.indirect
uri = URI.parse url
request = Net::HTTP::Get.new uri
request['Authorization'] = link.authorization if link.authorization
result = Net::HTTP.start uri.host, uri.port, use_ssl: uri.scheme == 'https' do |http|
http.request request
end
url = result.header['Location']
unless url
return redirect_to root_url, alert: t('.indirect_no_location')
end
end
redirect_to url, status: 302
end
end

View File

@ -0,0 +1,5 @@
class Link < ApplicationRecord
belongs_to :workgroup
scope :ordered, -> { order(:name) }
end

View File

@ -0,0 +1,6 @@
de:
links:
show:
indirect_no_location: Die konfigurierte URL hat keinen Location Header für die Weiterleitung zurück gegeben.
navigation:
links: Links

View File

@ -0,0 +1,6 @@
en:
links:
show:
indirect_no_location: The configured URL did not return a Location header for redirection.
navigation:
links: Links

View File

@ -0,0 +1,9 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
resources :links, only: [:show]
end
end

View File

@ -0,0 +1,11 @@
class CreateLinks < ActiveRecord::Migration
def change
create_table :links do |t|
t.string :name, null: false
t.string :url, null: false
t.references :workgroup
t.boolean :indirect, null: false, default: false
t.string :authorization
end
end
end

View File

@ -0,0 +1,21 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_links/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_links"
s.version = FoodsoftLinks::VERSION
s.authors = ["paroga"]
s.email = ["paroga@paroga.com"]
s.homepage = "https://github.com/foodcoops/foodsoft"
s.summary = "Links plugin for foodsoft."
s.description = "Adds simple link management to foodsoft."
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails"
s.add_dependency "deface", "~> 1.0"
end

View File

@ -0,0 +1 @@
require 'foodsoft_links/engine'

View File

@ -0,0 +1,27 @@
module FoodsoftLinks
class Engine < ::Rails::Engine
def navigation(primary, context)
primary.item :links, I18n.t('navigation.links'), '#', if: Proc.new { visble_links(context).any? } do |subnav|
visble_links(context).each do |link|
subnav.item link.id, link.name, context.link_path(link)
end
end
# move to left before admin item
if i = primary.items.index(primary[:admin])
primary.items.insert(i, primary.items.delete_at(-1))
end
end
def visble_links(context)
ret = Link.ordered
current_user = context.current_user
unless current_user.role_admin?
workgroups = current_user.workgroups.map(&:id)
ret = ret.where(workgroup: [nil] + workgroups)
end
ret
end
end
end

View File

@ -0,0 +1,3 @@
module FoodsoftLinks
VERSION = "0.0.1"
end