Merge pull request #167 from travis-ci/rkh-v3-enable-disable

v3: repo enable/disable
This commit is contained in:
Konstantin Haase 2015-03-06 17:32:56 +01:00
commit 47c6cf1ee6
13 changed files with 231 additions and 4 deletions

View File

@ -8,5 +8,9 @@ module Travis::API::V3
def self.for_request(*)
new
end
def admin_for(repository)
raise LoginRequired
end
end
end

View File

@ -2,12 +2,13 @@ require 'travis/api/v3/access_control/generic'
module Travis::API::V3
class AccessControl::Application < AccessControl::Generic
attr_reader :application_name, :config, :user
attr_reader :application_name, :config, :user, :user_control
def initialize(application_name, user: nil)
@application_name = application_name
@config = Travis.config.applications[application_name]
@user = user
@user_control = user ? AccessControl::User.new(user) : AccessControl::Generic.new
raise ArgumentError, 'unknown application %p' % application_name unless config
raise ArgumentError, 'cannot use %p without a user' % application_name if config.requires_user and not user
end
@ -19,5 +20,12 @@ module Travis::API::V3
def full_access?
config.full_access
end
def admin_for(repository)
return user_control.admin_for(repository) unless full_access?
admin = repository.users.where('permissions.admin = true'.freeze).order('users.synced_at DESC'.freeze).first
raise AdminAccessRequired, "no admin found for #{repository.slug}" unless admin
admin
end
end
end

View File

@ -15,6 +15,10 @@ module Travis::API::V3
full_access? or dispatch(object)
end
def admin_for(repository)
raise AdminAccessRequired, repository: repository
end
def user
end

View File

@ -15,6 +15,10 @@ module Travis::API::V3
true
end
def admin_for(repository)
permission?(:admin, repository) ? user : super
end
protected
def repository_writable?(repository)

View File

@ -0,0 +1,109 @@
require 'securerandom'
require 'base64'
module Travis::API::V3
module Extensions
class EncryptedColumn
attr_reader :disable, :options
alias disabled? disable
def initialize(options = {})
@options = options || {}
@disable = self.options[:disable]
@key = self.options[:key]
end
def enabled?
!disabled?
end
def load(data)
return nil unless data
data = data.to_s
decrypt?(data) ? decrypt(data) : data
end
def dump(data)
encrypt?(data) ? encrypt(data.to_s) : data
end
def key
@key || config.key
end
def iv
SecureRandom.hex(8)
end
def prefix
'--ENCR--'
end
def decrypt?(data)
data.present? && (!use_prefix? || prefix_used?(data))
end
def encrypt?(data)
data.present? && enabled?
end
def prefix_used?(data)
data[0..7] == prefix
end
def decrypt(data)
data = data[8..-1] if prefix_used?(data)
data = decode data
iv = data[-16..-1]
data = data[0..-17]
aes = create_aes :decrypt, key.to_s, iv
result = aes.update(data) + aes.final
end
def encrypt(data)
iv = self.iv
aes = create_aes :encrypt, key.to_s, iv
encrypted = aes.update(data) + aes.final
encrypted = "#{encrypted}#{iv}"
encrypted = encode encrypted
encrypted = "#{prefix}#{encrypted}" if use_prefix?
encrypted
end
def use_prefix?
options.has_key?(:use_prefix) ? options[:use_prefix] : Travis::Features.feature_inactive?(:db_encryption_prefix)
end
def create_aes(mode = :encrypt, key, iv)
aes = OpenSSL::Cipher::AES.new(256, :CBC)
aes.send(mode)
aes.key = key
aes.iv = iv
aes
end
def config
Travis.config.encryption
end
def decode(str)
Base64.strict_decode64 str
end
def encode(str)
Base64.strict_encode64 str
end
end
end
end

View File

@ -0,0 +1,44 @@
require 'gh'
module Travis::API::V3
class GitHub
DEFAULT_OPTIONS = {
client_id: Travis.config.oauth2.try(:client_id),
client_secret: Travis.config.oauth2.try(:client_secret),
user_agent: "Travis-API/3 Travis-CI/0.0.1 GH/#{GH::VERSION}",
origin: Travis.config.host,
api_url: Travis.config.github.api_url,
ssl: Travis.config.ssl.merge(Travis.config.github.ssl || {}).to_hash.compact
}
private_constant :DEFAULT_OPTIONS
attr_reader :gh, :user
def initialize(user = nil, token = nil)
if user.respond_to? :github_oauth_token
raise ServerError, 'no GitHub token for user' if user.github_oauth_token.blank?
token = user.github_oauth_token
end
@user = user
@gh = GH.with(token: token, **DEFAULT_OPTIONS)
end
def set_hook(repository, flag)
hooks_url = "repos/#{repository.slug}/hooks"
payload = {
name: 'travis'.freeze,
events: [:push, :pull_request, :issue_comment, :public, :member],
active: flag,
config: { domain: Travis.config.service_hook_url || '' }
}
if hook = gh[hooks_url].detect { |hook| hook['name'.freeze] == 'travis'.freeze }
gh.patch(hook['_links'.freeze]['self'.freeze]['href'.freeze], payload)
else
gh.post(hooks_url, payload)
end
end
end
end

View File

@ -0,0 +1,14 @@
module Travis::API::V3
class Models::Token < Model
belongs_to :user
validate :token, presence: true
serialize :token, Extensions::EncryptedColumn.new(disable: true)
before_validation :generate_token, on: :create
protected
def generate_token
self.token = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
end
end
end

View File

@ -1,9 +1,17 @@
module Travis::API::V3
class Models::User < Model
has_many :memberships, dependent: :destroy
has_many :organizations, through: :memberships
has_many :permissions, dependent: :destroy
has_many :repositories, through: :permissions
has_many :emails, dependent: :destroy
has_many :tokens, dependent: :destroy
has_many :repositories, through: :permissions
has_many :organizations, through: :memberships
serialize :github_oauth_token, Extensions::EncryptedColumn.new(disable: true)
def token
tokens.first_or_create.token
end
end
end

View File

@ -7,6 +7,9 @@ module Travis::API::V3
route '/repo/{repository.id}'
get :find
post :enable, '/enable'
post :disable, '/disable'
resource :requests do
route '/requests'
get :find

View File

@ -13,12 +13,17 @@ module Travis::API::V3
@access_control = access_control
@params = params
@queries = {}
@github = {}
end
def query(type = self.class.result_type)
@queries[type] ||= Queries[type].new(params, self.class.result_type)
end
def github(user = nil)
@github[user] ||= GitHub.new(user)
end
def find(type = self.class.result_type, *args)
not_found(true, type) unless object = query(type).find(*args)
not_found(false, type) unless access_control.visible? object

View File

@ -0,0 +1,15 @@
module Travis::API::V3
class Services::Repository::Disable < Service
def run!(activate = false)
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
raise NotFound unless repository = find(:repository)
admin = access_control.admin_for(repository)
github(admin).set_hook(repository, activate)
repository.update_attributes(active: activate)
repository
end
end
end

View File

@ -0,0 +1,7 @@
module Travis::API::V3
class Services::Repository::Enable < Services::Repository::Disable
def run!
super(true)
end
end
end

View File

@ -9,7 +9,9 @@ describe Travis::API::V3::ServiceIndex do
describe "custom json entry point" do
let(:expected_resources) {{
"repository" => {
"find" => [{"request-method"=>"GET", "uri-template"=>"#{path}repo/{repository.id}"}] },
"find" => [{"request-method"=>"GET", "uri-template"=>"#{path}repo/{repository.id}"}],
"enable" => [{"request-method"=>"POST", "uri-template"=>"#{path}repo/{repository.id}/enable"}],
"disable" => [{"request-method"=>"POST", "uri-template"=>"#{path}repo/{repository.id}/disable"}] },
"repositories" => {
"for_current_user" => [{"request-method"=>"GET", "uri-template"=>"#{path}repos"}] },
"branch" => {