Add Discourse plugin

This commit is contained in:
Patrick Gansterer 2017-09-22 01:14:48 +02:00
parent f8662c9184
commit 0edc780ec7
16 changed files with 221 additions and 8 deletions

View File

@ -53,6 +53,7 @@ gem 'ruby-filemagic'
gem 'acts_as_versioned', git: 'https://github.com/technoweenie/acts_as_versioned.git'
gem 'foodsoft_wiki', path: 'plugins/wiki'
gem 'foodsoft_messages', path: 'plugins/messages'
gem 'foodsoft_discourse', path: 'plugins/discourse'
# plugins not enabled by default
#gem 'foodsoft_current_orders', path: 'plugins/current_orders'

View File

@ -11,6 +11,13 @@ GIT
acts_as_versioned (0.6.0)
activerecord (>= 3.0.9)
PATH
remote: plugins/discourse
specs:
foodsoft_discourse (0.0.1)
deface (~> 1.0)
rails
PATH
remote: plugins/messages
specs:
@ -496,6 +503,7 @@ DEPENDENCIES
exception_notification
factory_girl_rails
faker
foodsoft_discourse!
foodsoft_messages!
foodsoft_wiki!
gaffe

View File

@ -38,6 +38,17 @@ class ApplicationController < ActionController::Base
session[:locale] = user.locale
end
def login_and_redirect_to_return_to(user, *args)
login user
if session[:return_to].present?
redirect_to_url = session[:return_to]
session[:return_to] = nil
else
redirect_to_url = root_url
end
redirect_to redirect_to_url, *args
end
def logout
session[:user_id] = nil
session[:return_to] = nil

View File

@ -10,14 +10,7 @@ class SessionsController < ApplicationController
user = User.authenticate(params[:nick], params[:password])
if user
user.update_attribute(:last_login, Time.now)
login user
if session[:return_to].present?
redirect_to_url = session[:return_to]
session[:return_to] = nil
else
redirect_to_url = root_url
end
redirect_to redirect_to_url, :notice => I18n.t('sessions.logged_in')
login_and_redirect_to_return_to user, :notice => I18n.t('sessions.logged_in')
else
flash.now.alert = I18n.t(FoodsoftConfig[:use_nick] ? 'sessions.login_invalid_nick' : 'sessions.login_invalid_email')
render "new"

View File

@ -0,0 +1,22 @@
FoodsoftDiscourse
=================
This plugin adds the possibility to log in via Discourse. A new button is added
to the login screen.
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_discourse', path: 'plugins/foodsoft_discourse'
```
This plugin introduces the foodcoop config option `discourse_url`, which takes
the URL fo the Discourse installation (e.g. `https://forum.example.com`) and the
config option `discourse_sso_secret`, which must be set to the same values as
configured in the `sso secret` setting of the Discourse installation. The plugin
will be disabled if not both config options are set.
This plugin is part of the foodsoft package and uses the GPL-3 license (see
foodsoft's LICENSE for the full license text).

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 = 'FoodsoftDiscourse'
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,52 @@
class DiscourseController < ApplicationController
before_filter -> { require_plugin_enabled FoodsoftDiscourse }
skip_before_filter :authenticate
def initiate
discourse_url = FoodsoftConfig[:discourse_url]
nonce = SecureRandom.hex()
return_sso_url = url_for(action: :callback, only_path: false)
payload = "nonce=#{nonce}&return_sso_url=#{return_sso_url}"
base64_payload = Base64.encode64 payload
sso = URI.escape base64_payload
sig = get_hmac_hex_string base64_payload
session[:discourse_sso_nonce] = nonce
redirect_to "#{discourse_url}/session/sso_provider?sso=#{sso}&sig=#{sig}"
end
def callback
raise I18n.t('discourse.callback.invalid_signature') if get_hmac_hex_string(params[:sso]) != params[:sig]
info = Rack::Utils.parse_query(Base64.decode64(params[:sso]))
info.symbolize_keys!
raise I18n.t('discourse.callback.invalid_nonce') if info[:nonce] != session[:discourse_sso_nonce]
session[:discourse_sso_nonce] = nil
id = info[:external_id].to_i
user = User.find_or_initialize_by(id: id) do |user|
user.id = id
user.password = SecureRandom.random_bytes(25)
end
user.nick = info[:username]
user.email = info[:email]
user.first_name = info[:name]
user.last_name = ''
user.last_login = Time.now
user.save!
login_and_redirect_to_return_to user, :notice => I18n.t('discourse.callback.logged_in')
rescue => error
redirect_to login_url, :alert => error.to_s
end
private
def get_hmac_hex_string payload
discourse_sso_secret = FoodsoftConfig[:discourse_sso_secret]
OpenSSL::HMAC.hexdigest 'sha256', discourse_sso_secret, payload
end
end

View File

@ -0,0 +1,5 @@
/ insert_after 'noscript'
- if FoodsoftDiscourse.enabled?
.center
= link_to t('.discourse_login'), discourse_initiate_path, class: 'btn btn-primary'
%hr

View File

@ -0,0 +1,6 @@
de:
discourse:
callback:
invalid_nonce: Ungültige nonce
invalid_signature: Ungültige Signature
logged_in: Angemeldet!

View File

@ -0,0 +1,6 @@
en:
discourse:
callback:
invalid_nonce: Invalid nonce
invalid_signature: Invalid signature
logged_in: Logged in!

View File

@ -0,0 +1,10 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
get '/discourse/callback' => 'discourse#callback'
get '/discourse/initiate' => 'discourse#initiate'
end
end

View File

@ -0,0 +1,21 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_discourse/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_discourse"
s.version = FoodsoftDiscourse::VERSION
s.authors = ["paroga"]
s.email = ["paroga@paroga.com"]
s.homepage = "https://github.com/foodcoops/foodsoft"
s.summary = "Discourse plugin for foodsoft."
s.description = "Allow SSO login via Discourse"
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,8 @@
require 'foodsoft_discourse/engine'
require 'foodsoft_discourse/redirect_to_login'
module FoodsoftDiscourse
def self.enabled?
FoodsoftConfig[:discourse_url] && FoodsoftConfig[:discourse_sso_secret]
end
end

View File

@ -0,0 +1,4 @@
module FoodsoftDiscourse
class Engine < ::Rails::Engine
end
end

View File

@ -0,0 +1,23 @@
module FoodsoftDiscourse
module RedirectToLogin
def self.included(base) # :nodoc:
base.class_eval do
alias orig_redirect_to_login redirect_to_login
def redirect_to_login(options={})
return orig_redirect_to_login(options) unless FoodsoftDiscourse.enabled?
redirect_to discourse_initiate_path
end
end
end
end
end
# modify existing helper
ActiveSupport.on_load(:after_initialize) do
ApplicationController.send :include, FoodsoftDiscourse::RedirectToLogin
end

View File

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