Add OAuth scopes
https://github.com/foodcoops/foodsoft/issues/582#issuecomment-442513237
This commit is contained in:
parent
02f1940694
commit
e9be38b3e9
12 changed files with 162 additions and 32 deletions
|
|
@ -1,4 +1,6 @@
|
|||
class Api::V1::BaseController < ApplicationController
|
||||
include Concerns::AuthApi
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
before_action :skip_session
|
||||
|
|
@ -11,16 +13,6 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
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)
|
||||
|
|
@ -28,7 +20,9 @@ class Api::V1::BaseController < ApplicationController
|
|||
|
||||
def require_ordergroup
|
||||
authenticate
|
||||
raise Api::Errors::PermissionRequired unless current_user.ordergroup.present?
|
||||
unless current_ordergroup.present?
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, must be in an ordergroup')
|
||||
end
|
||||
end
|
||||
|
||||
def skip_session
|
||||
|
|
@ -42,13 +36,18 @@ class Api::V1::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def not_acceptable_handler(e)
|
||||
render status: 422, json: {error: 'not_acceptable', error_description: e.message || 'Data not acceptable' }
|
||||
msg = e.message || 'Data not acceptable'
|
||||
render status: 422, json: {error: 'not_acceptable', error_description: msg}
|
||||
end
|
||||
|
||||
def doorkeeper_unauthorized_render_options(error:)
|
||||
{json: {error: error.name, error_description: error.description}}
|
||||
end
|
||||
|
||||
def doorkeeper_forbidden_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}
|
||||
|
|
@ -58,5 +57,4 @@ class Api::V1::BaseController < ApplicationController
|
|||
def show_user(user = current_user, **options)
|
||||
user.display
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
class Api::V1::ConfigsController < Api::V1::BaseController
|
||||
|
||||
before_action ->{ doorkeeper_authorize! 'config:user', 'config:read', 'config:write' }
|
||||
|
||||
def show
|
||||
render json: FoodsoftConfig, serializer: ConfigSerializer, root: 'config'
|
||||
end
|
||||
|
|
|
|||
9
app/controllers/api/v1/user/users_controller.rb
Normal file
9
app/controllers/api/v1/user/users_controller.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class Api::V1::User::UsersController < Api::V1::BaseController
|
||||
|
||||
before_action ->{ doorkeeper_authorize! 'user:read', 'user:write' }
|
||||
|
||||
def show
|
||||
render json: current_user
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
class Api::V1::UsersController < Api::V1::BaseController
|
||||
|
||||
def show
|
||||
render json: current_user
|
||||
end
|
||||
|
||||
end
|
||||
66
app/controllers/concerns/auth_api.rb
Normal file
66
app/controllers/concerns/auth_api.rb
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Controller concern for authentication methods
|
||||
#
|
||||
# Split off from main +ApplicationController+ to allow e.g.
|
||||
# Doorkeeper to use it too.
|
||||
module Concerns::AuthApi
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
protected
|
||||
|
||||
def authenticate
|
||||
# authenticate does not look at scopes, we just want to know if there's a valid token
|
||||
# @see https://github.com/doorkeeper-gem/doorkeeper/blob/v5.0.1/lib/doorkeeper/models/access_token_mixin.rb#L218
|
||||
doorkeeper_render_error unless doorkeeper_token && doorkeeper_token.accessible?
|
||||
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
|
||||
|
||||
def doorkeeper_authorize!(*scopes)
|
||||
super(*scopes)
|
||||
# In addition to Doorkeeper's authorization and scope check, we also verify
|
||||
# that the user has permissions for the scope (through its workgroups).
|
||||
# Unless no scopes were supplied, which means we only want to make sure there
|
||||
# is a valid user.
|
||||
#
|
||||
# If Doorkeeper's +handle_auth_errors+ is set to +:raise+, we don't get here,
|
||||
# but otherwise we need to check whether +super+ rendered an error.
|
||||
doorkeeper_authorize_roles!(*scopes) if scopes.present? && !performed?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Make sure that at least one the given OAuth scopes is valid for the current user's permissions.
|
||||
# @raise Api::Errors::PermissionsRequired
|
||||
def doorkeeper_authorize_roles!(*scopes)
|
||||
unless scopes.any? {|scope| doorkeeper_scope_permitted?(scope) }
|
||||
raise Api::Errors::PermissionRequired.new('Forbidden, no permission')
|
||||
end
|
||||
end
|
||||
|
||||
# Check whether a given OAuth scope is permitted for the current user.
|
||||
# @note This does not validate whether the given scope is a valid scope.
|
||||
# @return [Boolean] Whether the given scope is valid for the current user.
|
||||
# @see https://github.com/foodcoops/foodsoft/issues/582#issuecomment-442513237
|
||||
def doorkeeper_scope_permitted?(scope)
|
||||
scope_parts = scope.split(':')
|
||||
# user sub-scopes like +config:user+ are always permitted
|
||||
if scope_parts.last == 'user'
|
||||
return true
|
||||
end
|
||||
|
||||
case scope_parts.first
|
||||
when 'user' then true # access to the current user's own profile
|
||||
when 'config' then current_user.role_admin?
|
||||
when 'users' then current_user.role_admin?
|
||||
when 'workgroups' then current_user.role_admin?
|
||||
when 'suppliers' then current_user.role_suppliers?
|
||||
when 'group_orders' then current_user.role_orders?
|
||||
when 'finance' then current_user.role_finance?
|
||||
# please note that offline_access does not belong here, since it is not used for permission checking
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue