Prepare for API v1 (PR #570)

This commit is contained in:
wvengen 2018-10-13 20:16:35 +02:00 committed by GitHub
parent d9ae0d11b0
commit fd96b6ccc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 536 additions and 217 deletions

View file

@ -3,7 +3,6 @@ source "https://rubygems.org"
gem "rails", '~> 4.2'
gem 'sass-rails'
gem 'less-rails'
gem 'uglifier', '>= 1.0.3'
@ -30,6 +29,9 @@ gem 'simple_form'
gem 'inherited_resources'
gem 'localize_input', git: "https://github.com/bennibu/localize_input.git"
gem 'daemons'
gem 'doorkeeper'
gem 'doorkeeper-i18n'
gem 'rack-cors', require: 'rack/cors'
gem 'twitter-bootstrap-rails', '~> 2.2.8'
gem 'simple-navigation', '~> 3.14.0' # 3.x for simple_navigation_bootstrap
gem 'simple-navigation-bootstrap'

View file

@ -148,6 +148,9 @@ GEM
diff-lcs (1.3)
diffy (3.2.1)
docile (1.3.1)
doorkeeper (5.0.1)
railties (>= 4.2)
doorkeeper-i18n (4.0.0)
email_reply_trimmer (0.1.12)
erubi (1.7.1)
erubis (2.7.0)
@ -284,6 +287,7 @@ GEM
rack (1.6.10)
rack-contrib (1.8.0)
rack (~> 1.4)
rack-cors (1.0.2)
rack-protection (1.5.5)
rack
rack-test (0.6.3)
@ -498,6 +502,8 @@ DEPENDENCIES
daemons
database_cleaner
date_time_attribute
doorkeeper
doorkeeper-i18n
exception_notification
factory_bot_rails
faker
@ -526,6 +532,7 @@ DEPENDENCIES
pry-rescue
pry-stack_explorer
quiet_assets
rack-cors
rails (~> 4.2)
rails-assets-listjs (= 0.2.0.beta.4)
rails-i18n

View file

@ -1,10 +1,10 @@
class Admin::BaseController < ApplicationController
before_filter :authenticate_admin
def index
@user = self.current_user
@user = current_user
@groups = Group.where(deleted_at: nil).order('created_on DESC').limit(10)
@users = User.order('created_on DESC').limit(10)
end
end

View file

@ -0,0 +1,60 @@
class Api::V1::BaseController < ApplicationController
protect_from_forgery with: :null_session
before_action :skip_session
rescue_from ActiveRecord::RecordNotFound, with: :not_found_handler
rescue_from ActiveRecord::RecordNotSaved, with: :not_acceptable_handler
rescue_from ActiveRecord::RecordInvalid, with: :not_acceptable_handler
rescue_from Api::Errors::PermissionRequired, with: :permission_required_handler
private
def authenticate
doorkeeper_authorize!
super if current_user
end
# @return [User] Current user, or +nil+ if no valid token.
def current_user
@current_user ||= User.undeleted.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
# @return [Ordergroup] Current user's ordergroup, or +nil+ if no valid token or user has no ordergroup.
def current_ordergroup
current_user.try(:ordergroup)
end
def require_ordergroup
authenticate
raise Api::Errors::PermissionRequired unless current_user.ordergroup.present?
end
def skip_session
request.session_options[:skip] = true
end
def not_found_handler(e)
# remove where-clauses from error message (not suitable for end-users)
msg = e.message.try {|m| m.sub(/\s*\[.*?\]\s*$/, '')} || 'Not found'
render status: 404, json: {error: 'not_found', error_description: msg}
end
def not_acceptable_handler(e)
render status: 422, json: {error: 'not_acceptable', error_description: e.message || 'Data not acceptable' }
end
def doorkeeper_unauthorized_render_options(error:)
{json: {error: error.name, error_description: error.description}}
end
def permission_required_handler(e)
msg = e.message || 'Forbidden, user has no access'
render status: 403, json: {error: 'forbidden', error_description: msg}
end
# @todo something with ApplicationHelper#show_user
def show_user(user = current_user, **options)
user.display
end
end

View file

@ -1,142 +1,24 @@
# encoding: utf-8
class ApplicationController < ActionController::Base
include Foodsoft::ControllerExtensions::Locale
include Concerns::FoodcoopScope
include Concerns::Auth
include Concerns::Locale
helper_method :current_user
helper_method :available_locales
protect_from_forgery
before_filter :select_foodcoop, :authenticate, :set_user_last_activity, :store_controller, :items_per_page
before_filter :authenticate, :set_user_last_activity, :store_controller, :items_per_page
after_filter :remove_controller
around_filter :set_time_zone, :set_currency
# Returns the controller handling the current request.
def self.current
Thread.current[:application_controller]
end
protected
def current_user
# check if there is a valid session and return the logged-in user (its object)
if session[:user_id] && params[:foodcoop]
# for shared-host installations. check if the cookie-subdomain fits to request.
@current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
end
end
helper_method :current_user
def deny_access
session[:return_to] = request.original_url
redirect_to root_url, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path))
end
private
def login(user)
session[:user_id] = user.id
session[:scope] = FoodsoftConfig.scope # Save scope in session to not allow switching between foodcoops with one account
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
end
def authenticate(role = 'any')
# Attempt to retrieve authenticated user from controller instance or session...
if !current_user
# No user at all: redirect to login page.
logout
session[:return_to] = request.original_url
redirect_to_login :alert => I18n.t('application.controller.error_authn')
else
# We have an authenticated user, now check role...
# Roles gets the user through his memberships.
hasRole = case role
when "admin" then current_user.role_admin?
when "finance" then current_user.role_finance?
when "article_meta" then current_user.role_article_meta?
when "pickups" then current_user.role_pickups?
when "suppliers" then current_user.role_suppliers?
when "orders" then current_user.role_orders?
when "finance_or_orders" then (current_user.role_finance? || current_user.role_orders?)
when "pickups_or_orders" then (current_user.role_pickups? || current_user.role_orders?)
when "any" then true # no role required
else false # any unknown role will always fail
end
if hasRole
current_user
else
deny_access
end
end
end
def authenticate_admin
authenticate('admin')
end
def authenticate_finance
authenticate('finance')
end
def authenticate_article_meta
authenticate('article_meta')
end
def authenticate_pickups
authenticate('pickups')
end
def authenticate_suppliers
authenticate('suppliers')
end
def authenticate_orders
authenticate('orders')
end
def authenticate_finance_or_orders
authenticate('finance_or_orders')
end
def authenticate_pickups_or_orders
authenticate('pickups_or_orders')
end
# checks if the current_user is member of given group.
# if fails the user will redirected to startpage
def authenticate_membership_or_admin(group_id = params[:id])
@group = Group.find(group_id)
unless @group.member?(@current_user) || @current_user.role_admin?
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
end
end
def authenticate_or_token(prefix, role = 'any')
if not params[:token].blank?
begin
TokenVerifier.new(prefix).verify(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature
redirect_to root_path, alert: I18n.t('application.controller.error_token')
end
else
authenticate(role)
end
end
def set_user_last_activity
if current_user && (session[:last_activity] == nil || session[:last_activity] < 1.minutes.ago)
current_user.update_attribute(:last_activity, Time.now)
@ -167,16 +49,11 @@ class ApplicationController < ActionController::Base
redirect_to root_path, alert: I18n.t('application.controller.error_feature_disabled')
end
# Redirect to the login page, used in authenticate, plugins can override this.
def redirect_to_login(options={})
redirect_to login_url, options
end
# Stores this controller instance as a thread local varibale to be accessible from outside ActionController/ActionView.
def store_controller
Thread.current[:application_controller] = self
end
# Sets the thread local variable that holds a reference to the current controller to nil.
def remove_controller
Thread.current[:application_controller] = nil
@ -187,23 +64,6 @@ class ApplicationController < ActionController::Base
@supplier = Supplier.find(params[:supplier_id]) if params[:supplier_id]
end
# Set config and database connection for each request
# It uses the subdomain to select the appropriate section in the config files
# Use this method as a before filter (first filter!) in ApplicationController
def select_foodcoop
return unless FoodsoftConfig[:multi_coop_install]
foodcoop = params[:foodcoop]
if foodcoop.blank?
FoodsoftConfig.select_default_foodcoop
redirect_to root_url
elsif FoodsoftConfig.allowed_foodcoop? foodcoop
FoodsoftConfig.select_foodcoop foodcoop
else
raise ActionController::RoutingError.new 'Foodcoop Not Found'
end
end
def items_per_page
if params[:per_page] && params[:per_page].to_i > 0 && params[:per_page].to_i <= 500
@per_page = params[:per_page].to_i
@ -212,11 +72,6 @@ class ApplicationController < ActionController::Base
end
end
# Always stay in foodcoop url scope
def default_url_options(options = {})
{foodcoop: FoodsoftConfig.scope}
end
# Set timezone according to foodcoop preference.
# @see http://stackoverflow.com/questions/4362663/timezone-with-rails-3
# @see http://archives.ryandaigle.com/articles/2008/1/25/what-s-new-in-edge-rails-easier-timezones

View file

@ -0,0 +1,148 @@
# Controller concern for authentication methods
#
# Split off from main +ApplicationController+ to allow e.g.
# Doorkeeper to use it too.
module Concerns::Auth
extend ActiveSupport::Concern
protected
def current_user
# check if there is a valid session and return the logged-in user (its object)
if session[:user_id] && params[:foodcoop]
# for shared-host installations. check if the cookie-subdomain fits to request.
@current_user ||= User.undeleted.find_by_id(session[:user_id]) if session[:scope] == FoodsoftConfig.scope
end
end
def deny_access
session[:return_to] = request.original_url
redirect_to root_url, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path))
end
private
def login(user)
session[:user_id] = user.id
session[:scope] = FoodsoftConfig.scope # Save scope in session to not allow switching between foodcoops with one account
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
expire_access_tokens
end
def authenticate(role = 'any')
# Attempt to retrieve authenticated user from controller instance or session...
if !current_user
# No user at all: redirect to login page.
logout
session[:return_to] = request.original_url
redirect_to_login :alert => I18n.t('application.controller.error_authn')
else
# We have an authenticated user, now check role...
# Roles gets the user through his memberships.
hasRole = case role
when 'admin' then current_user.role_admin?
when 'finance' then current_user.role_finance?
when 'article_meta' then current_user.role_article_meta?
when 'pickups' then current_user.role_pickups?
when 'suppliers' then current_user.role_suppliers?
when 'orders' then current_user.role_orders?
when 'finance_or_orders' then (current_user.role_finance? || current_user.role_orders?)
when 'pickups_or_orders' then (current_user.role_pickups? || current_user.role_orders?)
when 'any' then true # no role required
else false # any unknown role will always fail
end
if hasRole
current_user
else
deny_access
end
end
end
def authenticate_admin
authenticate('admin')
end
def authenticate_finance
authenticate('finance')
end
def authenticate_article_meta
authenticate('article_meta')
end
def authenticate_pickups
authenticate('pickups')
end
def authenticate_suppliers
authenticate('suppliers')
end
def authenticate_orders
authenticate('orders')
end
def authenticate_finance_or_orders
authenticate('finance_or_orders')
end
def authenticate_pickups_or_orders
authenticate('pickups_or_orders')
end
# checks if the current_user is member of given group.
# if fails the user will redirected to startpage
def authenticate_membership_or_admin(group_id = params[:id])
@group = Group.find(group_id)
unless @group.member?(@current_user) || @current_user.role_admin?
redirect_to root_path, alert: I18n.t('application.controller.error_members_only')
end
end
def authenticate_or_token(prefix, role = 'any')
if not params[:token].blank?
begin
TokenVerifier.new(prefix).verify(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature
redirect_to root_path, alert: I18n.t('application.controller.error_token')
end
else
authenticate(role)
end
end
# Expires any access tokens for the current user (unless they have the 'offline_access' scope)
# @see https://github.com/doorkeeper-gem/doorkeeper/issues/71#issuecomment-5471317
def expire_access_tokens
return unless @current_user
Doorkeeper::AccessToken.transaction do
token_scope = Doorkeeper::AccessToken.where(revoked_at: nil, resource_owner_id: @current_user.id)
token_scope.each do |token|
token.destroy! unless token.scopes.include?('offline_access')
end
end
end
# Redirect to the login page, used in authenticate, plugins can override this.
def redirect_to_login(options={})
redirect_to login_url, options
end
end

View file

@ -0,0 +1,36 @@
# Controller concern to handle foodcoop scope
#
# Includes a +before_filter+ for selecting foodcoop from url.
#
module Concerns::FoodcoopScope
extend ActiveSupport::Concern
included do
before_filter :select_foodcoop
end
private
# Set config and database connection for each request
# It uses the subdomain to select the appropriate section in the config files
# Use this method as a before filter (first filter!) in ApplicationController
def select_foodcoop
return unless FoodsoftConfig[:multi_coop_install]
foodcoop = params[:foodcoop]
if foodcoop.blank?
FoodsoftConfig.select_default_foodcoop
redirect_to root_url
elsif FoodsoftConfig.allowed_foodcoop? foodcoop
FoodsoftConfig.select_foodcoop foodcoop
else
raise ActionController::RoutingError.new 'Foodcoop Not Found'
end
end
# Always stay in foodcoop url scope
def default_url_options(options = {})
super().merge({foodcoop: FoodsoftConfig.scope})
end
end

View file

@ -0,0 +1,51 @@
module Concerns::Locale
extend ActiveSupport::Concern
included do
before_filter :set_locale
end
def explicitly_requested_language
params[:locale]
end
def user_settings_language
current_user&.locale
end
def session_language
session[:locale]
end
def browser_language
request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil
end
def default_language
FoodsoftConfig[:default_locale] || ::I18n.default_locale
end
private
def select_language_according_to_priority
language = explicitly_requested_language || session_language || user_settings_language
language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale]
language.presence&.to_sym unless language.blank?
end
def available_locales
::I18n.available_locales
end
def set_locale
if available_locales.include?(select_language_according_to_priority)
::I18n.locale = select_language_according_to_priority
else
::I18n.locale = default_language
end
locale = session[:locale] = ::I18n.locale
logger.info("Set locale to #{locale}")
end
end

View file

@ -6,6 +6,8 @@
- url = action_name == 'show' ? nil : admin_config_path(tab: tab)
%li{class: ('active' if @current_tab==tab)}= link_to t("config.tabs.#{tab}"), "#{url}#tab-#{tab}", data: ({toggle: 'tab'} unless url)
-# make this a button to give some indicator that navigation away might lose changes
-# make these buttons to give some indicator that navigation away might lose changes
%li.pull-right{class: ('active' if @current_tab=='list')}
= link_to t('config.tabs.list'), list_admin_config_path, class: ('btn' unless @current_tab=='list')
%li.pull-right
= link_to t('config.tabs.applications'), oauth_applications_path, class: 'btn'

View file

@ -6,6 +6,7 @@ default: &defaults
multi_coop_install: false
# If multi_coop_install you have to use a coop name, which you you wanna be selected by default
# Please use basic characters for scope names, matching /[-a-zA-Z0-9_]+/
default_scope: 'f'
# name of this foodcoop

View file

@ -48,7 +48,7 @@ module Foodsoft
# parameters by using an attr_accessible or attr_protected declaration.
# TODO Re-activate this. Uncommenting this line will currently cause rspec to fail.
config.active_record.whitelist_attributes = false
# Enable the asset pipeline
config.assets.enabled = true
@ -62,6 +62,14 @@ module Foodsoft
# Load legacy scripts from vendor
config.assets.precompile += [ 'vendor/assets/javascripts/*.js' ]
# CORS for API
config.middleware.insert_before 0, 'Rack::Cors' do
allow do
origins '*'
# this restricts Foodsoft scopes to certain characters - let's discuss it when it becomes an actual problem
resource %r{\A/[-a-zA-Z0-9_]+/api/v1/}, headers: :any, methods: :any
end
end
end
# Foodsoft version

View file

@ -0,0 +1,113 @@
Doorkeeper.configure do
# Change the ORM that doorkeeper will use (needs plugins)
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
authenticate
end
resource_owner_from_credentials do
User.authenticate(params[:username], params[:password])
end
admin_authenticator do
authenticate_admin
end
# Authorization Code expiration time (default 10 minutes).
# authorization_code_expires_in 10.minutes
# Access token expiration time (default 2 hours).
# If you want to disable expiration, set this to nil.
# access_token_expires_in 2.hours
# Assign a custom TTL for implicit grants.
# custom_access_token_expires_in do |oauth_client|
# oauth_client.application.additional_settings.implicit_oauth_expiration
# end
# Use a custom class for generating the access token.
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
# access_token_generator "::Doorkeeper::JWT"
# Reuse access token for the same resource owner within an application (disabled by default)
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
# reuse_access_token
# Issue access tokens with refresh token (disabled by default)
use_refresh_token
# Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter :confirmation => true (default false) if you want to enforce ownership of
# a registered application
# Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
# enable_application_owner :confirmation => false
# Define access token scopes for your provider
# For more information go to
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
# default_scopes :public
# optional_scopes :write, :update
# Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
# Check out the wiki for more information on customization
# client_credentials :from_basic, :from_params
# Change the way access token is authenticated from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
# Check out the wiki for more information on customization
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
# Change the native redirect uri for client apps
# When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
#
# native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
# by default in non-development environments). OAuth2 delegates security in
# communication to the HTTPS protocol so it is wise to keep this enabled.
#
# force_ssl_in_redirect_uri !Rails.env.development?
# Specify what grant flows are enabled in array of Strings. The valid
# strings and the flows they enable are:
#
# "authorization_code" => Authorization Code Grant Flow
# "implicit" => Implicit Grant Flow
# "password" => Resource Owner Password Credentials Grant Flow
# "client_credentials" => Client Credentials Grant Flow
#
# If not specified, Doorkeeper enables authorization_code and
# client_credentials.
#
# implicit and password grant flows have risks that you should understand
# before enabling:
# http://tools.ietf.org/html/rfc6819#section-4.4.2
# http://tools.ietf.org/html/rfc6819#section-4.4.3
#
grant_flows %w(authorization_code implicit password)
# Under some circumstances you might want to have applications auto-approved,
# so that the user skips the authorization step.
# For example if dealing with a trusted application.
# skip_authorization do |resource_owner, client|
# client.superapp? or resource_owner.admin?
# end
skip_authorization { true } # right now only admins can add apps, so this is ok
# WWW-Authenticate Realm (default "Doorkeeper").
realm 'Foodsoft'
end
# my take on https://github.com/doorkeeper-gem/doorkeeper/issues/465
ActiveSupport.on_load(:after_initialize) do
Doorkeeper::ApplicationController.send :include, Concerns::Locale
Doorkeeper::ApplicationController.send :include, Concerns::FoodcoopScope
Doorkeeper::ApplicationController.send :include, Concerns::Auth
end

View file

@ -663,6 +663,7 @@ de:
use_wiki: Wiki verwenden
webstats_tracking_code: Code für Websiteanalysetool
tabs:
applications: Apps
foodcoop: Foodcoop
language: Sprache
layout: Layout

View file

@ -666,6 +666,7 @@ en:
use_wiki: Enable wiki
webstats_tracking_code: Tracking code
tabs:
applications: Apps
foodcoop: Foodcoop
language: Language
layout: Layout

View file

@ -638,6 +638,7 @@ nl:
use_wiki: Wiki gebruiken
webstats_tracking_code: Tracking code
tabs:
applications: Apps
foodcoop: Foodcoop
language: Taal
layout: Layout

View file

@ -10,6 +10,8 @@ Foodsoft::Application.routes.draw do
scope '/:foodcoop' do
use_doorkeeper
# Root path
root to: 'home#index'

View file

@ -0,0 +1,42 @@
class CreateDoorkeeperTables < ActiveRecord::Migration
def change
create_table :oauth_applications do |t|
t.string :name, null: false
t.string :uid, null: false
t.string :secret, null: false
t.text :redirect_uri, null: false
t.string :scopes, null: false, default: ''
t.timestamps
end
add_index :oauth_applications, :uid, unique: true
create_table :oauth_access_grants do |t|
t.integer :resource_owner_id, null: false
t.integer :application_id, null: false
t.string :token, null: false
t.integer :expires_in, null: false
t.text :redirect_uri, null: false
t.datetime :created_at, null: false
t.datetime :revoked_at
t.string :scopes
end
add_index :oauth_access_grants, :token, unique: true
create_table :oauth_access_tokens do |t|
t.integer :resource_owner_id
t.integer :application_id
t.string :token, null: false
t.string :refresh_token
t.integer :expires_in
t.datetime :revoked_at
t.datetime :created_at, null: false
t.string :scopes
end
add_index :oauth_access_tokens, :token, unique: true
add_index :oauth_access_tokens, :resource_owner_id
add_index :oauth_access_tokens, :refresh_token, unique: true
end
end

View file

@ -262,6 +262,46 @@ ActiveRecord::Schema.define(version: 20171201000000) do
t.binary "received_email"
end
create_table "oauth_access_grants", force: :cascade do |t|
t.integer "resource_owner_id", limit: 4, null: false
t.integer "application_id", limit: 4, null: false
t.string "token", limit: 255, null: false
t.integer "expires_in", limit: 4, null: false
t.text "redirect_uri", limit: 65535, null: false
t.datetime "created_at", null: false
t.datetime "revoked_at"
t.string "scopes", limit: 255
end
add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
create_table "oauth_access_tokens", force: :cascade do |t|
t.integer "resource_owner_id", limit: 4
t.integer "application_id", limit: 4
t.string "token", limit: 255, null: false
t.string "refresh_token", limit: 255
t.integer "expires_in", limit: 4
t.datetime "revoked_at"
t.datetime "created_at", null: false
t.string "scopes", limit: 255
end
add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
create_table "oauth_applications", force: :cascade do |t|
t.string "name", limit: 255, null: false
t.string "uid", limit: 255, null: false
t.string "secret", limit: 255, null: false
t.text "redirect_uri", limit: 65535, null: false
t.string "scopes", limit: 255, default: "", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "order_articles", force: :cascade do |t|
t.integer "order_id", limit: 4, default: 0, null: false
t.integer "article_id", limit: 4, default: 0, null: false

5
lib/api/errors.rb Normal file
View file

@ -0,0 +1,5 @@
module Api::Errors
class Error < StandardError; end
# Authentication is handled by Doorkeeper, so no errors for that here
class PermissionRequired < Error; end
end

View file

@ -1,56 +0,0 @@
# -*- encoding : utf-8 -*-
module Foodsoft
module ControllerExtensions
module Locale
extend ActiveSupport::Concern
included do
before_filter :set_locale
end
def explicitly_requested_language
params[:locale]
end
def user_settings_language
current_user.locale if current_user
end
def session_language
session[:locale]
end
def browser_language
request.env['HTTP_ACCEPT_LANGUAGE'] ? request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first : nil
end
def default_language
FoodsoftConfig[:default_locale] or ::I18n.default_locale
end
protected
def select_language_according_to_priority
language = explicitly_requested_language || session_language || user_settings_language
language ||= browser_language unless FoodsoftConfig[:ignore_browser_locale]
language.to_sym unless language.blank?
end
def available_locales
::I18n.available_locales
end
def set_locale
if available_locales.include?(select_language_according_to_priority)
::I18n.locale = select_language_according_to_priority
else
::I18n.locale = default_language
end
locale = session[:locale] = ::I18n.locale
logger.info("Set locale to #{locale}")
end
end
end
end

View file

@ -4,13 +4,13 @@ module FoodsoftDiscourse
def self.included(base) # :nodoc:
base.class_eval do
alias orig_redirect_to_login redirect_to_login
alias foodsoft_discourse_orig_redirect_to_login redirect_to_login
def redirect_to_login(options={})
if FoodsoftDiscourse.enabled? && !FoodsoftConfig[:discourse_sso]
redirect_to discourse_initiate_path
else
orig_redirect_to_login(options)
foodsoft_discourse_orig_redirect_to_login(options)
end
end
@ -22,5 +22,5 @@ end
# modify existing helper
ActiveSupport.on_load(:after_initialize) do
ApplicationController.send :include, FoodsoftDiscourse::RedirectToLogin
Concerns::Auth.send :include, FoodsoftDiscourse::RedirectToLogin
end