From 44261a5d9fd5b78f8a44fe330e2386525f4c3437 Mon Sep 17 00:00:00 2001
From: Valery Sizov <vsv2711@gmail.com>
Date: Wed, 9 Sep 2015 17:36:01 +0300
Subject: [PATCH] integration with gitlab auth

---
 app/controllers/ci/application_controller.rb  | 61 +++++--------------
 app/controllers/ci/builds_controller.rb       |  2 +-
 app/controllers/ci/commits_controller.rb      |  2 +-
 app/controllers/ci/projects_controller.rb     | 11 ++--
 .../ci/user_sessions_controller.rb            | 10 ---
 app/helpers/ci/commits_helper.rb              |  2 +-
 app/models/ability.rb                         |  1 +
 app/models/ci/user.rb                         | 32 +---------
 app/views/ci/builds/_build.html.haml          |  2 +-
 app/views/ci/builds/show.html.haml            | 12 ++--
 app/views/ci/commits/_commit.html.haml        |  2 +-
 app/views/ci/commits/show.html.haml           |  4 +-
 app/views/ci/projects/_gl_projects.html.haml  |  2 +-
 app/views/ci/projects/gitlab.html.haml        |  4 --
 app/views/ci/projects/show.html.haml          |  2 +-
 app/views/ci/user_sessions/show.html.haml     |  2 +-
 app/views/layouts/ci/_info.html.haml          |  2 +-
 app/views/layouts/ci/_nav.html.haml           |  6 +-
 app/views/layouts/ci/project.html.haml        |  2 +-
 lib/ci/api/projects.rb                        | 10 +--
 spec/models/ci/user_spec.rb                   | 50 ---------------
 21 files changed, 51 insertions(+), 170 deletions(-)

diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb
index 95390d09737..e5c99066a68 100644
--- a/app/controllers/ci/application_controller.rb
+++ b/app/controllers/ci/application_controller.rb
@@ -1,5 +1,5 @@
 module Ci
-  class ApplicationController < ActionController::Base
+  class ApplicationController < ::ApplicationController
     def self.railtie_helpers_paths
       "app/helpers/ci"
     end
@@ -9,49 +9,19 @@ module Ci
     rescue_from Ci::Network::UnauthorizedError, with: :invalid_token
     before_filter :default_headers
     #before_filter :check_config
+    helper_method :gl_project
 
     protect_from_forgery
 
-    helper_method :current_user
-    before_filter :reset_cache
-
     private
 
-    def current_user
-      @current_user ||= session[:ci_current_user]
-    end
-
-    def sign_in(user)
-      session[:ci_current_user] = user
-    end
-
-    def sign_out
-      reset_session
-    end
-
-    def authenticate_user!
-      unless current_user
-        redirect_to new_ci_user_sessions_path
-        return
-      end
-    end
-
-    def authenticate_admin!
-      unless current_user && current_user.is_admin
-        redirect_to new_ci_user_sessions_path
-        return
-      end
-    end
-
     def authenticate_public_page!
       unless project.public
         unless current_user
-          redirect_to(new_ci_user_sessions_path(state: generate_oauth_state(request.fullpath))) and return
+          redirect_to(new_user_sessions_path) and return
         end
 
-        unless current_user.can_access_project?(project.gitlab_id)
-          page_404 and return
-        end
+        return access_denied! unless can?(current_user, :read_project, gl_project)
       end
     end
 
@@ -62,19 +32,23 @@ module Ci
     end
 
     def authorize_access_project!
-      unless current_user.can_access_project?(@project.gitlab_id)
+      unless can?(current_user, :read_project, gl_project)
         return page_404
       end
     end
 
-    def authorize_project_developer!
-      unless current_user.has_developer_access?(@project.gitlab_id)
+    def authorize_manage_builds!
+      unless can?(current_user, :manage_builds, gl_project)
         return page_404
       end
     end
 
+    def authenticate_admin!
+      return render_404 unless current_user.is_admin?
+    end
+
     def authorize_manage_project!
-      unless current_user.can_manage_project?(@project.gitlab_id)
+      unless can?(current_user, :manage_project, gl_project)
         return page_404
       end
     end
@@ -83,13 +57,6 @@ module Ci
       render file: "#{Rails.root}/public/404.html", status: 404, layout: false
     end
 
-    # Reset user cache every day for security purposes
-    def reset_cache
-      if current_user && current_user.sync_at < (Time.zone.now - 24.hours)
-        current_user.reset_cache
-      end
-    end
-
     def default_headers
       headers['X-Frame-Options'] = 'DENY'
       headers['X-XSS-Protection'] = '1; mode=block'
@@ -129,5 +96,9 @@ module Ci
       reset_session
       redirect_to ci_root_path
     end
+
+    def gl_project
+      ::Project.find(@project.gitlab_id)
+    end
   end
 end
diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb
index eeff3f1e0a0..28fad3671f7 100644
--- a/app/controllers/ci/builds_controller.rb
+++ b/app/controllers/ci/builds_controller.rb
@@ -5,7 +5,7 @@ module Ci
     before_filter :project
     before_filter :authorize_access_project!, except: [:status, :show]
     before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel]
-    before_filter :authorize_project_developer!, only: [:retry, :cancel]
+    before_filter :authorize_manage_builds!, only: [:retry, :cancel]
     before_filter :build, except: [:show]
 
     def show
diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb
index 9f74a2fd807..bad9075dde6 100644
--- a/app/controllers/ci/commits_controller.rb
+++ b/app/controllers/ci/commits_controller.rb
@@ -4,7 +4,7 @@ module Ci
     before_filter :authenticate_public_page!, only: :show
     before_filter :project
     before_filter :authorize_access_project!, except: [:status, :show, :cancel]
-    before_filter :authorize_project_developer!, only: [:cancel]
+    before_filter :authorize_manage_builds!, only: [:cancel]
     before_filter :commit, only: :show
 
     def show
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 6ff7fc9f77a..80a5e602171 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -21,12 +21,15 @@ module Ci
       @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i
       @page = @offset == 0 ? 1 : (@offset / @limit + 1)
 
-      current_user.reset_cache if params[:reset_cache]
+      @gl_projects = current_user.authorized_projects
+      @gl_projects = @gl_projects.where("name LIKE %?%", params[:search]) if params[:search]
+      @gl_projects = @gl_projects.page(@page).per(@limit)
 
-      @gl_projects = current_user.gitlab_projects(params[:search], @page, @limit)
       @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date
       @total_count = @gl_projects.size
-      @gl_projects.reject! { |gl_project| @projects.map(&:gitlab_id).include?(gl_project.id) }
+      
+      @gl_projects = @gl_projects.where.not(id: @projects.map(&:gitlab_id))
+
       respond_to do |format|
         format.json do
           pager_json("ci/projects/gitlab", @total_count)
@@ -52,7 +55,7 @@ module Ci
     def create
       project_data = OpenStruct.new(JSON.parse(params["project"]))
 
-      unless current_user.can_manage_project?(project_data.id)
+      unless can?(current_user, :manage_project, ::Project.find(project_data.id))
         return redirect_to ci_root_path, alert: 'You have to have at least master role to enable CI for this project'
       end
 
diff --git a/app/controllers/ci/user_sessions_controller.rb b/app/controllers/ci/user_sessions_controller.rb
index 82134c1f7ba..818e1fcdea1 100644
--- a/app/controllers/ci/user_sessions_controller.rb
+++ b/app/controllers/ci/user_sessions_controller.rb
@@ -10,11 +10,6 @@ module Ci
     end
 
     def auth
-      unless is_oauth_state_valid?(params[:state])
-        redirect_to new_ci_user_sessions_path
-        return
-      end
-
       redirect_to client.auth_code.authorize_url({
         redirect_uri: callback_ci_user_sessions_url,
         state: params[:state]
@@ -22,11 +17,6 @@ module Ci
     end
 
     def callback
-      unless is_oauth_state_valid?(params[:state])
-        redirect_to new_ci_user_sessions_path
-        return
-      end
-
       token = client.auth_code.get_token(params[:code], redirect_uri: callback_ci_user_sessions_url).token
       
       @user_session = Ci::UserSession.new
diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb
index 0479bc10594..748d12138b1 100644
--- a/app/helpers/ci/commits_helper.rb
+++ b/app/helpers/ci/commits_helper.rb
@@ -16,7 +16,7 @@ module Ci
     end
 
     def commit_link(commit)
-      link_to(commit.short_sha, ci_project_ref_commit_path(commit.project, commit.ref, commit.sha))
+      link_to(commit.short_sha, ci_project_ref_commits_path(commit.project, commit.ref, commit.sha))
     end
 
     def truncate_first_line(message, length = 50)
diff --git a/app/models/ability.rb b/app/models/ability.rb
index f8e5afa9b01..a020b24a550 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -149,6 +149,7 @@ class Ability
         :admin_merge_request,
         :create_merge_request,
         :create_wiki,
+        :manage_builds,
         :push_code
       ]
     end
diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb
index 49ec7126fc1..f7e2eaae1f1 100644
--- a/app/models/ci/user.rb
+++ b/app/models/ci/user.rb
@@ -39,43 +39,13 @@ module Ci
       @sync_at = Time.now
     end
 
-    def can_access_project?(project_gitlab_id)
-      !!project_info(project_gitlab_id)
-    end
-
-    # Indicate if user has developer access or higher
-    def has_developer_access?(project_gitlab_id)
-      data = project_info(project_gitlab_id)
-
-      return false unless data && data["permissions"]
-
-      permissions = data["permissions"]
-
-      if permissions["project_access"] && permissions["project_access"]["access_level"] >= DEVELOPER_ACCESS
-        return true
-      end
-
-      if permissions["group_access"] && permissions["group_access"]["access_level"] >= DEVELOPER_ACCESS
-        return true
-      end
-    end
-
-    def can_manage_project?(project_gitlab_id)
-      Rails.cache.fetch(cache_key('manage', project_gitlab_id, sync_at)) do
-        !!Ci::Network.new.project_hooks(authenticate_options, project_gitlab_id)
-      end
-    end
-
     def authorized_runners
       Ci::Runner.specific.includes(:runner_projects).
         where(Ci::RunnerProject.table_name => { project_id: authorized_projects } )
     end
 
     def authorized_projects
-      Ci::Project.where(gitlab_id: gitlab_projects.map(&:id)).select do |project|
-        # This is slow: it makes request to GitLab for each project to verify manage permission
-        can_manage_project?(project.gitlab_id)
-      end
+      Ci::Project.where(gitlab_id: current_user.authorized_projects.map(&:id))
     end
 
     def authenticate_options
diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml
index 54ca1102b5c..da306c9f020 100644
--- a/app/views/ci/builds/_build.html.haml
+++ b/app/views/ci/builds/_build.html.haml
@@ -35,7 +35,7 @@
         #{build.coverage}%
 
   %td
-    - if defined?(controls) && current_user && current_user.has_developer_access?(@project.gitlab_id)
+    - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project)
       .pull-right
         - if build.active?
           = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do
diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml
index 0bef67d8a20..a698176c3ed 100644
--- a/app/views/ci/builds/show.html.haml
+++ b/app/views/ci/builds/show.html.haml
@@ -1,10 +1,10 @@
 %h4.page-title
-  = link_to @project.name, @project
+  = link_to ci_project_path(@project)
   @
   = @commit.short_sha
   
 %p
-  = link_to ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) do
+  = link_to ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) do
     &larr; Back to project commit
 %hr
 #up-build-trace
@@ -12,7 +12,7 @@
   %ul.nav.nav-tabs.append-bottom-10
     - @commit.builds_without_retry_sorted.each do |build|
       %li{class: ('active' if build == @build) }
-        = link_to ci_build_url(build) do
+        = link_to ci_project_build_url(@project, build) do
           %i{class: build_icon_css_class(build)}
           %span
             Build ##{build.id}
@@ -84,7 +84,7 @@
     .build-widget
       %h4.title
         Build
-        - if current_user && current_user.has_developer_access?(@project.gitlab_id)
+        - if current_user && can?(current_user, :manage_builds, gl_project)
           .pull-right
             - if @build.active?
               = link_to "Cancel", cancel_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-danger'
@@ -161,7 +161,7 @@
           - @builds.each_with_index do |build, i|
             %tr.build.alert{class: build_status_alert_class(build)}
               %td
-                = link_to ci_build_url(build) do
+                = link_to ci_project_build_url(@project, build) do
                   %span ##{build.id}
               %td
                 - if build.name
@@ -173,4 +173,4 @@
 
 
 :javascript
-  new CiBuild("#{ci_build_url(@build)}", "#{@build.status}")
+  new CiBuild("#{ci_project_build_url(@project, @build)}", "#{@build.status}")
diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml
index a955a5b6479..c1b1988d147 100644
--- a/app/views/ci/commits/_commit.html.haml
+++ b/app/views/ci/commits/_commit.html.haml
@@ -7,7 +7,7 @@
 
 
   %td.build-link
-    = link_to ci_project_ref_commit_path(commit.project, commit.ref, commit.sha) do
+    = link_to ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) do
       %strong #{commit.short_sha}
 
   %td.build-message
diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml
index 832cc6a1bae..9b597b45aa5 100644
--- a/app/views/ci/commits/show.html.haml
+++ b/app/views/ci/commits/show.html.haml
@@ -33,10 +33,10 @@
           %span.attr-name Created at:
           #{@commit.created_at.to_s(:short)}
 
-- if current_user && current_user.has_developer_access?(@project.gitlab_id)
+- if current_user && can?(current_user, :manage_builds, gl_project)
   .pull-right
     - if @commit.builds.running_or_pending.any?
-      = link_to "Cancel", cancel_ci_project_ref_commit_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger'
+      = link_to "Cancel", cancel_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger'
 
 
 - if @commit.yaml_errors.present?
diff --git a/app/views/ci/projects/_gl_projects.html.haml b/app/views/ci/projects/_gl_projects.html.haml
index 6ed19e13887..f63d8246e96 100644
--- a/app/views/ci/projects/_gl_projects.html.haml
+++ b/app/views/ci/projects/_gl_projects.html.haml
@@ -11,5 +11,5 @@
           Added
       - else
         = form_tag ci_projects_path do
-          = hidden_field_tag :project, project.to_h.to_json
+          = hidden_field_tag :project, project.to_json
           = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm'
diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml
index bd55b1f12e7..0344676680e 100644
--- a/app/views/ci/projects/gitlab.html.haml
+++ b/app/views/ci/projects/gitlab.html.haml
@@ -4,10 +4,6 @@
       Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink})
       - if params[:search].present?
         by keyword: "#{params[:search]}",
-      #{time_ago_in_words(current_user.sync_at)} ago.
-      = link_to gitlab_ci_projects_path(reset_cache: true, search: params[:search]), class: 'sync-now btn btn-sm btn-default reset-cache' do
-        %i.fa.fa-refresh
-        Sync now
       %br
 
     .pull-right
diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml
index 27899591391..b79ab957ba8 100644
--- a/app/views/ci/projects/show.html.haml
+++ b/app/views/ci/projects/show.html.haml
@@ -1,6 +1,6 @@
 = render 'ci/shared/guide' unless @project.setup_finished?
 
-- if current_user && current_user.can_manage_project?(@project.gitlab_id) && !@project.any_runners?
+- if current_user && can?(current_user, :manage_project, gl_project) && !@project.any_runners?
   .alert.alert-danger
     Builds for this project wont be served unless you configure runners on
     = link_to "Runners page", ci_project_runners_path(@project)
diff --git a/app/views/ci/user_sessions/show.html.haml b/app/views/ci/user_sessions/show.html.haml
index 43f64a429b2..0710e205618 100644
--- a/app/views/ci/user_sessions/show.html.haml
+++ b/app/views/ci/user_sessions/show.html.haml
@@ -2,7 +2,7 @@
 %h3
   Hi, #{@user.name}
 
-  - if @user.is_admin
+  - if @user.is_admin?
     %span.label.label-success Admin
 
 .profile-block
diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml
index bce3ce77031..9bc23cfad05 100644
--- a/app/views/layouts/ci/_info.html.haml
+++ b/app/views/layouts/ci/_info.html.haml
@@ -5,5 +5,5 @@
     - if notice
       .alert.alert-info= notice
 
-  - if current_user && current_user.is_admin && Ci::Runner.count.zero?
+  - if current_user && current_user.is_admin? && Ci::Runner.count.zero?
     = render 'ci/shared/no_runners'
diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml
index babd14ca2d3..4a0e45a0217 100644
--- a/app/views/layouts/ci/_nav.html.haml
+++ b/app/views/layouts/ci/_nav.html.haml
@@ -9,7 +9,7 @@
 
     .collapse.navbar-collapse
       %ul.nav.navbar-nav
-        - if current_user && current_user.is_admin
+        - if current_user && current_user.is_admin?
           %li
             = link_to ci_admin_projects_path do
               Admin
@@ -19,12 +19,12 @@
       %ul.nav.navbar-nav.pull-right
         - if current_user
           %li
-            = link_to ci_user_sessions_path do
+            = link_to new_user_session_path do
               .profile-holder
                 = image_tag user_avatar_url(current_user, 64), class: 'avatar s32', alt: ''
                 %span= current_user.name
           %li
-            = link_to ci_user_sessions_path, class: "logout", method: :delete do
+            = link_to destroy_user_session_path, class: "logout", method: :delete do
               %i.fa.fa-signout
               Logout
         - else
diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml
index 763a7fc0b02..9549485d095 100644
--- a/app/views/layouts/ci/project.html.haml
+++ b/app/views/layouts/ci/project.html.haml
@@ -16,7 +16,7 @@
           = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' )
     %hr
     .container
-      - if current_user && current_user.can_manage_project?(@project.gitlab_id)
+      - if current_user && can?(current_user, :manage_project, gl_project)
         .row
           .col-md-2.append-bottom-20
             = render 'layouts/ci/nav_project'
diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb
index f9b4937c033..bdcacecf6ab 100644
--- a/lib/ci/api/projects.rb
+++ b/lib/ci/api/projects.rb
@@ -66,7 +66,7 @@ module Ci
         get ":id" do
           project = Ci::Project.find(params[:id])
 
-          unauthorized! unless current_user.can_access_project?(project.gitlab_id)
+          unauthorized! unless can?(current_user, :read_project, gl_project)
 
           present project, with: Entities::Project
         end
@@ -118,7 +118,7 @@ module Ci
         put ":id" do
           project = Ci::Project.find(params[:id])
 
-          unauthorized! unless current_user.can_manage_project?(project.gitlab_id)
+          unauthorized! unless can?(current_user, :manage_project, gl_project)
 
           attrs = attributes_for_keys [:name, :gitlab_id, :path, :gitlab_url, :default_ref, :ssh_url_to_repo]
 
@@ -144,7 +144,7 @@ module Ci
         delete ":id" do
           project = Ci::Project.find(params[:id])
 
-          unauthorized! unless current_user.can_manage_project?(project.gitlab_id)
+          unauthorized! unless can?(current_user, :manage_project, gl_project)
 
           project.destroy
         end
@@ -160,7 +160,7 @@ module Ci
           project = Ci::Project.find(params[:id])
           runner  = Ci::Runner.find(params[:runner_id])
 
-          unauthorized! unless current_user.can_manage_project?(project.gitlab_id)
+          unauthorized! unless can?(current_user, :manage_project, gl_project)
 
           options = {
             project_id: project.id,
@@ -188,7 +188,7 @@ module Ci
           project = Ci::Project.find(params[:id])
           runner  = Ci::Runner.find(params[:runner_id])
 
-          unauthorized! unless current_user.can_manage_project?(project.gitlab_id)
+          unauthorized! unless can?(current_user, :manage_project, gl_project)
 
           options = {
             project_id: project.id,
diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb
index d1b87988b74..c4d7b3ccae5 100644
--- a/spec/models/ci/user_spec.rb
+++ b/spec/models/ci/user_spec.rb
@@ -2,56 +2,6 @@ require 'spec_helper'
 
 describe Ci::User do
 
-  describe "has_developer_access?" do
-    before do
-      @user = User.new({})
-    end
-
-    let(:project_with_owner_access) do
-      {
-        "name" => "gitlab-shell",
-        "permissions" => {
-          "project_access" => {
-            "access_level"=> 10,
-            "notification_level" => 3
-          },
-          "group_access" => {
-            "access_level" => 50,
-            "notification_level" => 3
-          }
-        }
-      }
-    end
-
-    let(:project_with_reporter_access) do
-      {
-        "name" => "gitlab-shell",
-        "permissions" => {
-          "project_access" => {
-            "access_level" => 20,
-            "notification_level" => 3
-          },
-          "group_access" => {
-            "access_level" => 10,
-            "notification_level" => 3
-          }
-        }
-      }
-    end
-
-    it "returns false for reporter" do
-      @user.stub(:project_info).and_return(project_with_reporter_access)
-
-      @user.has_developer_access?(1).should be_false
-    end
-
-    it "returns true for owner" do
-      @user.stub(:project_info).and_return(project_with_owner_access)
-
-      @user.has_developer_access?(1).should be_true
-    end
-  end
-
   describe "authorized_projects" do
     let (:user) { User.new({}) }
 
-- 
GitLab