From 2b0ae177fa68454bf2079371e927f12734ead775 Mon Sep 17 00:00:00 2001
From: Konstantin Haase <konstantin.mailinglists@googlemail.com>
Date: Wed, 29 Apr 2015 11:31:05 +0200
Subject: [PATCH] v3: expose repo access permissions in payload

---
 lib/travis/api/v3/access_control/scoped.rb    | 13 ++++++++++--
 lib/travis/api/v3/permissions/generic.rb      | 11 +++++++---
 lib/travis/api/v3/renderer/model_renderer.rb  |  8 ++++++--
 spec/v3/services/owner/find_spec.rb           | 10 ++++++++++
 spec/v3/services/owner/repositories_spec.rb   |  5 +++++
 .../repositories/for_current_user_spec.rb     | 15 +++++++++-----
 spec/v3/services/repository/find_spec.rb      | 20 +++++++++++++++++++
 7 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/lib/travis/api/v3/access_control/scoped.rb b/lib/travis/api/v3/access_control/scoped.rb
index 5bf1df90..33b8017d 100644
--- a/lib/travis/api/v3/access_control/scoped.rb
+++ b/lib/travis/api/v3/access_control/scoped.rb
@@ -2,18 +2,27 @@ require 'travis/api/v3/access_control/generic'
 
 module Travis::API::V3
   class AccessControl::Scoped < AccessControl::Generic
-    attr_accessor :unscoped, :owner_name, :name
+    attr_accessor :unscoped, :anonymous, :owner_name, :name
 
     def initialize(scope, unscoped)
       @owner_name, @name = scope.split(?/.freeze, 2)
       @unscoped          = unscoped
+      @anonymous         = AccessControl::Anonymous.new
     end
 
     protected
 
     def private_repository_visible?(repository)
+      scope_repository(repository).visible?(repository)
+    end
+
+    def repository_writable?(repository)
+      scope_repository(repository).writable?(repository)
+    end
+
+    def scope_repository(repository, method = caller_locations.first.base_label)
       return false if name and repository.name != name
-      unscoped.visible?(repository) if repository.owner_name == owner_name
+      repository.owner_name == owner_name ? unscoped : anonymous
     end
   end
 end
diff --git a/lib/travis/api/v3/permissions/generic.rb b/lib/travis/api/v3/permissions/generic.rb
index ac270bfe..5648adfd 100644
--- a/lib/travis/api/v3/permissions/generic.rb
+++ b/lib/travis/api/v3/permissions/generic.rb
@@ -2,11 +2,12 @@ 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|
+        rights = superclass.respond_to?(:access_rights) ? superclass.access_rights.dup : {}
+        public_instance_methods(false).each do |method|
           next unless method.to_s =~ /^([^_].+)\?$/
-          rights << $1.to_sym
+          rights[$1.to_sym] = method
         end
+        rights
       end
     end
 
@@ -46,6 +47,10 @@ module Travis::API::V3
       access_control.visible? object
     end
 
+    def to_h
+      self.class.access_rights.map { |k,v| [k,!!public_send(v)] }.to_h
+    end
+
     private
 
     def write?
diff --git a/lib/travis/api/v3/renderer/model_renderer.rb b/lib/travis/api/v3/renderer/model_renderer.rb
index 491aa627..81b4a864 100644
--- a/lib/travis/api/v3/renderer/model_renderer.rb
+++ b/lib/travis/api/v3/renderer/model_renderer.rb
@@ -43,7 +43,7 @@ module Travis::API::V3
       @script_name    = script_name
       @include        = include
       @included       = included
-      @access_control = access_control
+      @access_control = access_control || AccessControl::Anonymous.new
     end
 
     def href
@@ -74,13 +74,17 @@ module Travis::API::V3
       nested_included = included + [model]
       modes           = {}
 
+      if permissions = access_control.permissions(model) and (representation != :minimal or include? :@permissions)
+        result[:@permissions] = permissions.to_h
+      end
+
       if include.any?
         excepted_type = result[:@type].to_s
         fields        = fields.dup
       end
 
       include.each do |qualified_field|
-        raise WrongParams, 'illegal format for include parameter'.freeze unless /\A(?<prefix>\w+)\.(?<field>\w+)\Z$/ =~ qualified_field
+        raise WrongParams, 'illegal format for include parameter'.freeze unless /\A(?<prefix>\w+)\.(?<field>@?\w+)\Z$/ =~ qualified_field
         next if prefix != excepted_type
         raise WrongParams, 'no field %p to include'.freeze % qualified_field unless self.class.available_attributes.include?(field)
 
diff --git a/spec/v3/services/owner/find_spec.rb b/spec/v3/services/owner/find_spec.rb
index 90152fd8..b5dd0c82 100644
--- a/spec/v3/services/owner/find_spec.rb
+++ b/spec/v3/services/owner/find_spec.rb
@@ -39,6 +39,11 @@ describe Travis::API::V3::Services::Owner::Find do
         "repositories"      => [{
           "@type"           => "repository",
           "@href"           => "/v3/repo/#{repo.id}",
+          "@permissions"    => {
+            "read"          => true,
+            "enable"        => false,
+            "disable"       => false,
+            "create_request"=> false},
           "id"              => repo.id,
           "name"            => "example-repo",
           "slug"            => "example-org/example-repo",
@@ -76,6 +81,11 @@ describe Travis::API::V3::Services::Owner::Find do
         "repositories"      => [{
           "@type"           => "repository",
           "@href"           => "/v3/repo/#{repo.id}",
+          "@permissions"    => {
+            "read"          => true,
+            "enable"        => false,
+            "disable"       => false,
+            "create_request"=> false},
           "id"              => repo.id,
           "name"            => "example-repo",
           "slug"            => "example-org/example-repo",
diff --git a/spec/v3/services/owner/repositories_spec.rb b/spec/v3/services/owner/repositories_spec.rb
index 9f47fecb..77285bfc 100644
--- a/spec/v3/services/owner/repositories_spec.rb
+++ b/spec/v3/services/owner/repositories_spec.rb
@@ -18,6 +18,11 @@ describe Travis::API::V3::Services::Owner::Repositories do
       "repositories"      => [{
         "@type"           => "repository",
         "@href"           => "/v3/repo/#{repo.id}",
+        "@permissions"    => {
+          "read"          => true,
+          "enable"        => false,
+          "disable"       => false,
+          "create_request"=> false},
         "id"              =>  repo.id,
         "name"            =>  "minimal",
         "slug"            =>  "svenfuchs/minimal",
diff --git a/spec/v3/services/repositories/for_current_user_spec.rb b/spec/v3/services/repositories/for_current_user_spec.rb
index 67068f69..26fe6151 100644
--- a/spec/v3/services/repositories/for_current_user_spec.rb
+++ b/spec/v3/services/repositories/for_current_user_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
 describe Travis::API::V3::Services::Repositories::ForCurrentUser do
   let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
 
-  let(:token)   { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
-  let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}"                        }}
-  before        { Permission.create(repository: repo, user: repo.owner, pull: true) }
-  before        { repo.update_attribute(:private, true)                             }
-  after         { repo.update_attribute(:private, false)                            }
+  let(:token)   { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1)             }
+  let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}"                                    }}
+  before        { Permission.create(repository: repo, user: repo.owner, pull: true, push: true) }
+  before        { repo.update_attribute(:private, true)                                         }
+  after         { repo.update_attribute(:private, false)                                        }
 
   describe "private repository, private API, authenticated as user with access" do
     before  { get("/v3/repos", {}, headers)    }
@@ -18,6 +18,11 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
       "repositories"      => [{
         "@type"           => "repository",
         "@href"           => "/v3/repo/#{repo.id}",
+        "@permissions"    => {
+          "read"          => true,
+          "enable"        => true,
+          "disable"       => true,
+          "create_request"=> true},
         "id"              =>  repo.id,
         "name"            =>  "minimal",
         "slug"            =>  "svenfuchs/minimal",
diff --git a/spec/v3/services/repository/find_spec.rb b/spec/v3/services/repository/find_spec.rb
index b92df8d0..d0d6d431 100644
--- a/spec/v3/services/repository/find_spec.rb
+++ b/spec/v3/services/repository/find_spec.rb
@@ -10,6 +10,11 @@ describe Travis::API::V3::Services::Repository::Find do
     example    { expect(parsed_body).to be == {
       "@type"           => "repository",
       "@href"           => "/v3/repo/#{repo.id}",
+      "@permissions"    => {
+        "read"          => true,
+        "enable"        => false,
+        "disable"       => false,
+        "create_request"=> false},
       "id"              =>  repo.id,
       "name"            =>  "minimal",
       "slug"            =>  "svenfuchs/minimal",
@@ -95,6 +100,11 @@ describe Travis::API::V3::Services::Repository::Find do
     example       { expect(parsed_body).to be == {
       "@type"           => "repository",
       "@href"           => "/v3/repo/#{repo.id}",
+      "@permissions"    => {
+        "read"          => true,
+        "enable"        => false,
+        "disable"       => false,
+        "create_request"=> false},
       "id"              =>  repo.id,
       "name"            =>  "minimal",
       "slug"            =>  "svenfuchs/minimal",
@@ -165,6 +175,11 @@ describe Travis::API::V3::Services::Repository::Find do
     example { expect(parsed_body).to be == {
       "@type"           => "repository",
       "@href"           => "/v3/repo/#{repo.id}",
+      "@permissions"    => {
+        "read"          => true,
+        "enable"        => true,
+        "disable"       => true,
+        "create_request"=> true},
       "id"              =>  repo.id,
       "name"            =>  "minimal",
       "slug"            =>  "svenfuchs/minimal",
@@ -241,6 +256,11 @@ describe Travis::API::V3::Services::Repository::Find do
     example { expect(parsed_body).to be == {
       "@type"           => "repository",
       "@href"           => "/v3/repo/#{repo.id}",
+      "@permissions"    => {
+        "read"          => true,
+        "enable"        => true,
+        "disable"       => true,
+        "create_request"=> true},
       "id"              =>  repo.id,
       "name"            =>  "minimal",
       "slug"            =>  "svenfuchs/minimal",