v3: introduce permission objects
This commit is contained in:
parent
283092ff92
commit
eac88e5d50
|
@ -30,7 +30,6 @@ module Travis
|
|||
WrongCredentials = ClientError .create('access denied', status: 403)
|
||||
LoginRequired = ClientError .create('login required', status: 403)
|
||||
InsufficientAccess = ClientError .create(status: 403)
|
||||
PushAccessRequired = InsufficientAccess .create('push access required')
|
||||
WrongParams = ClientError .create('wrong parameters')
|
||||
ServerError = Error .create(status: 500)
|
||||
NotImplemented = ServerError .create('request not (yet) implemented', status: 501)
|
||||
|
|
|
@ -36,6 +36,11 @@ module Travis::API::V3
|
|||
list.select { |r| visible?(r) }
|
||||
end
|
||||
|
||||
def permissions(object)
|
||||
return unless factory = permission_class(object.class)
|
||||
factory.new(self, object)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def build_visible?(build)
|
||||
|
@ -78,13 +83,22 @@ module Travis::API::V3
|
|||
send(method, object) if respond_to?(method, true)
|
||||
end
|
||||
|
||||
@@method_for_cache = Tool::ThreadLocal.new
|
||||
|
||||
@@unknown_permission = Object.new
|
||||
@@permission_class_cache = Tool::ThreadLocal.new
|
||||
@@method_for_cache = Tool::ThreadLocal.new
|
||||
|
||||
def permission_class(klass)
|
||||
result = @@permission_class_cache[klass] ||= Permissions[normailze_type(klass), false] || @@unknown_permission
|
||||
result unless result == @@unknown_permission
|
||||
end
|
||||
|
||||
def method_for(type, method)
|
||||
@@method_for_cache[[type, method]] ||= begin
|
||||
prefix = type.name.sub(/^Travis::API::V3::Models::/, ''.freeze).underscore
|
||||
"#{prefix}_#{method}"
|
||||
end
|
||||
@@method_for_cache[[type, method]] ||= "#{normailze_type(type)}_#{method}"
|
||||
end
|
||||
|
||||
def normailze_type(type)
|
||||
type.name.sub(/^Travis::API::V3::Models::/, ''.freeze).underscore.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,12 +2,12 @@ require 'travis/api/v3/access_control/generic'
|
|||
|
||||
module Travis::API::V3
|
||||
class AccessControl::User < AccessControl::Generic
|
||||
attr_reader :user, :permissions
|
||||
attr_reader :user, :access_permissions
|
||||
|
||||
def initialize(user)
|
||||
user = Models::User.find(user.id) if user.is_a? ::User
|
||||
@user = user
|
||||
@permissions = user.permissions.where(user_id: user.id)
|
||||
user = Models::User.find(user.id) if user.is_a? ::User
|
||||
@user = user
|
||||
@access_permissions = user.permissions.where(user_id: user.id)
|
||||
super()
|
||||
end
|
||||
|
||||
|
@ -20,7 +20,7 @@ module Travis::API::V3
|
|||
end
|
||||
|
||||
def visible_repositories(list)
|
||||
list.where('repositories.private = false OR repositories.id IN (?)'.freeze, permissions.map(&:repository_id))
|
||||
list.where('repositories.private = false OR repositories.id IN (?)'.freeze, access_permissions.map(&:repository_id))
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -35,7 +35,7 @@ module Travis::API::V3
|
|||
|
||||
def permission?(type, id)
|
||||
id = id.id if id.is_a? ::Repository
|
||||
permissions.where(type => true, :repository_id => id).any?
|
||||
access_permissions.where(type => true, :repository_id => id).any?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
5
lib/travis/api/v3/permissions.rb
Normal file
5
lib/travis/api/v3/permissions.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Travis::API::V3
|
||||
module Permissions
|
||||
extend ConstantResolver
|
||||
end
|
||||
end
|
55
lib/travis/api/v3/permissions/generic.rb
Normal file
55
lib/travis/api/v3/permissions/generic.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
module Travis::API::V3
|
||||
class Permissions::Generic
|
||||
def self.access_rights
|
||||
@access_rights ||= begin
|
||||
rights = superclass.respond_to?(:access_rights) ? superclass.access_rights.dup : []
|
||||
public_instance_methods(false) do |method|
|
||||
next unless method.to_s =~ /^([^_].+)\?$/
|
||||
rights << $1.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# for any public method defined with a question mark in the end, it defines a method with an
|
||||
# exclamation mark that will raise an InsufficientAccess error if the question mark version
|
||||
# returns false
|
||||
def self.method_added(method_name)
|
||||
super
|
||||
|
||||
return unless public_method_defined?(method_name)
|
||||
return unless method_name.to_s =~ /^([^_].+)\?$/
|
||||
|
||||
permission = $1
|
||||
type = name[/[^:]+$/].underscore
|
||||
|
||||
class_eval <<-RUBY
|
||||
def #{permission}!
|
||||
return self if #{permission}?
|
||||
payload = {
|
||||
resource_type: "#{type}".freeze,
|
||||
permission: "#{permission}".freeze
|
||||
}
|
||||
payload[:#{type}] = object if read?
|
||||
raise InsufficientAccess.new('operation requires #{permission} access to #{type}', payload)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
attr_accessor :access_control, :object
|
||||
|
||||
def initialize(access_control, object)
|
||||
@access_control = access_control
|
||||
@object = object
|
||||
end
|
||||
|
||||
def read?
|
||||
access_control.visible? object
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write?
|
||||
access_control.writable? object
|
||||
end
|
||||
end
|
||||
end
|
7
lib/travis/api/v3/permissions/repository.rb
Normal file
7
lib/travis/api/v3/permissions/repository.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Travis::API::V3
|
||||
class Permissions::Repository < Permissions::Generic
|
||||
def create_request?
|
||||
write?
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,9 +8,9 @@ module Travis::API::V3
|
|||
params "request", "user", :config, :message, :branch
|
||||
|
||||
def run
|
||||
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
|
||||
raise NotFound unless repository = find(:repository)
|
||||
raise PushAccessRequired, repository: repository unless access_control.writable?(repository)
|
||||
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
|
||||
raise NotFound unless repository = find(:repository)
|
||||
access_control.permissions(repository).create_request!
|
||||
|
||||
user = find(:user) if access_control.full_access? and params_for? 'user'.freeze
|
||||
user ||= access_control.user
|
||||
|
|
|
@ -49,8 +49,10 @@ describe Travis::API::V3::Services::Requests::Create do
|
|||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "push_access_required",
|
||||
"error_message" => "push access required",
|
||||
"error_type" => "insufficient_access",
|
||||
"error_message" => "operation requires create_request access to repository",
|
||||
"resource_type" => "repository",
|
||||
"permission" => "create_request",
|
||||
"repository" => {
|
||||
"@type" => "repository",
|
||||
"@href" => "/repo/#{repo.id}",
|
||||
|
|
Loading…
Reference in New Issue
Block a user