Commit fe43272f authored by Kirill Smelkov's avatar Kirill Smelkov

Sync with upstream 8-4-stable

* origin/8-4-stable:
  Version 8.4.4
  Merge branch 'variables-build-log' into 'master'
  Update CHANGELOG
  Merge branch 'backup-database-timeout-fix' into 'master'
  Fix build/permissions.feature tests adding missing steps
  Limit guest access builds
  Merge branch 'omniauth-saml-update' into 'master'
  Version 8.4.3
  Merge branch 'rs-relax-autosize' into 'master'
parents 6acfb924 9c31cc6f
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.4.4
- Update omniauth-saml gem to 1.4.2
- Prevent long-running backup tasks from timing out the database connection
- Add a Project setting to allow guests to view build logs (defaults to true)
v 8.4.3 v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger - Increase lfs_objects size column to 8-byte integer to allow files larger
than 2.1GB than 2.1GB
...@@ -7,6 +12,9 @@ v 8.4.3 ...@@ -7,6 +12,9 @@ v 8.4.3
- Fix highlighting in blame view - Fix highlighting in blame view
- Update sentry-raven gem to prevent "Not a git repository" console output - Update sentry-raven gem to prevent "Not a git repository" console output
when running certain commands when running certain commands
- Add instrumentation to additional Gitlab::Git and Rugged methods for
performance monitoring
- Allow autosize textareas to also be manually resized
v 8.4.2 v 8.4.2
- Bump required gitlab-workhorse version to bring in a fix for missing - Bump required gitlab-workhorse version to bring in a fix for missing
......
...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1' ...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-google-oauth2', '~> 0.2.0'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-saml', '~> 1.4.2'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
......
...@@ -534,9 +534,9 @@ GEM ...@@ -534,9 +534,9 @@ GEM
omniauth-oauth2 (1.3.1) omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0) oauth2 (~> 1.0)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-saml (1.4.1) omniauth-saml (1.4.2)
omniauth (~> 1.1) omniauth (~> 1.1)
ruby-saml (~> 1.0.0) ruby-saml (~> 1.1, >= 1.1.1)
omniauth-shibboleth (1.2.1) omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0) omniauth (>= 1.0.0)
omniauth-twitter (1.2.1) omniauth-twitter (1.2.1)
...@@ -692,7 +692,7 @@ GEM ...@@ -692,7 +692,7 @@ GEM
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.7.5)
ruby-saml (1.0.0) ruby-saml (1.1.1)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3) uuid (~> 2.3)
ruby2ruby (2.2.0) ruby2ruby (2.2.0)
...@@ -975,7 +975,7 @@ DEPENDENCIES ...@@ -975,7 +975,7 @@ DEPENDENCIES
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.2.0) omniauth-google-oauth2 (~> 0.2.0)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.4.0) omniauth-saml (~> 1.4.2)
omniauth-shibboleth (~> 1.2.0) omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
......
8.4.2 8.4.4
\ No newline at end of file \ No newline at end of file
#= require jquery.ba-resize
#= require autosize #= require autosize
$ -> $ ->
autosize($('.js-autosize')) $fields = $('.js-autosize')
$fields.on 'autosize:resized', ->
$field = $(@)
$field.data('height', $field.outerHeight())
$fields.on 'resize.autosize', ->
$field = $(@)
if $field.data('height') != $field.outerHeight()
$field.data('height', $field.outerHeight())
autosize.destroy($field)
$field.css('max-height', window.outerHeight)
autosize($fields)
autosize.update($fields)
$fields.css('resize', 'vertical')
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
background: #FFF; background: #FFF;
border: 1px solid #ddd; border: 1px solid #ddd;
min-height: 140px; min-height: 140px;
max-height: 430px; max-height: 500px;
padding: 5px; padding: 5px;
box-shadow: none; box-shadow: none;
width: 100%; width: 100%;
......
...@@ -147,7 +147,7 @@ ...@@ -147,7 +147,7 @@
.edit_note { .edit_note {
.markdown-area { .markdown-area {
min-height: 140px; min-height: 140px;
max-height: 430px; max-height: 500px;
} }
.note-form-actions { .note-form-actions {
background: transparent; background: transparent;
......
...@@ -3,52 +3,5 @@ module Ci ...@@ -3,52 +3,5 @@ module Ci
def self.railtie_helpers_paths def self.railtie_helpers_paths
"app/helpers/ci" "app/helpers/ci"
end end
private
def authorize_access_project!
unless can?(current_user, :read_project, project)
return page_404
end
end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return page_404
end
end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_manage_project!
unless can?(current_user, :admin_project, project)
return page_404
end
end
def page_404
render file: "#{Rails.root}/public/404.html", status: 404, layout: false
end
def default_headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
end
# JSON for infinite scroll via Pager object
def pager_json(partial, count)
html = render_to_string(
partial,
layout: false,
formats: [:html]
)
render json: {
html: html,
count: count
}
end
end end
end end
module Ci module Ci
class ProjectsController < Ci::ApplicationController class ProjectsController < Ci::ApplicationController
before_action :project, except: [:index] before_action :project
before_action :authenticate_user!, except: [:index, :build, :badge] before_action :authorize_read_project!, except: [:badge]
before_action :authorize_access_project!, except: [:index, :badge]
before_action :no_cache, only: [:badge] before_action :no_cache, only: [:badge]
protect_from_forgery protect_from_forgery
......
class Projects::ArtifactsController < Projects::ApplicationController class Projects::ArtifactsController < Projects::ApplicationController
layout 'project' layout 'project'
before_action :authorize_read_build_artifacts! before_action :authorize_read_build!
def download def download
unless artifacts_file.file_storage? unless artifacts_file.file_storage?
...@@ -43,14 +43,4 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -43,14 +43,4 @@ class Projects::ArtifactsController < Projects::ApplicationController
def artifacts_file def artifacts_file
@artifacts_file ||= build.artifacts_file @artifacts_file ||= build.artifacts_file
end end
def authorize_read_build_artifacts!
unless can?(current_user, :read_build_artifacts, @project)
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
end end
class Projects::BuildsController < Projects::ApplicationController class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all] before_action :build, except: [:index, :cancel_all]
before_action :authorize_manage_builds!, except: [:index, :show, :status] before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
before_action :authorize_update_build!, except: [:index, :show, :status]
layout "project" layout "project"
...@@ -69,10 +70,4 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -69,10 +70,4 @@ class Projects::BuildsController < Projects::ApplicationController
def build_path(build) def build_path(build)
namespace_project_build_path(build.project.namespace, build.project, build) namespace_project_build_path(build.project.namespace, build.project, build)
end end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return render_404
end
end
end end
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds] before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
before_action :authorize_manage_builds!, only: [:cancel_builds] before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds]
before_action :define_show_vars, only: [:show, :builds] before_action :define_show_vars, only: [:show, :builds]
def show def show
...@@ -77,10 +77,4 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -77,10 +77,4 @@ class Projects::CommitController < Projects::ApplicationController
@statuses = ci_commit.statuses if ci_commit @statuses = ci_commit.statuses if ci_commit
end end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return render_404
end
end
end end
class Projects::RunnerProjectsController < Projects::ApplicationController class Projects::RunnerProjectsController < Projects::ApplicationController
before_action :authorize_admin_project! before_action :authorize_admin_build!
layout 'project_settings' layout 'project_settings'
......
class Projects::RunnersController < Projects::ApplicationController class Projects::RunnersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
before_action :authorize_admin_project!
layout 'project_settings' layout 'project_settings'
......
class Projects::TriggersController < Projects::ApplicationController class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_project! before_action :authorize_admin_build!
layout 'project_settings' layout 'project_settings'
......
class Projects::VariablesController < Projects::ApplicationController class Projects::VariablesController < Projects::ApplicationController
before_action :authorize_admin_project! before_action :authorize_admin_build!
layout 'project_settings' layout 'project_settings'
......
...@@ -223,6 +223,7 @@ class ProjectsController < ApplicationController ...@@ -223,6 +223,7 @@ class ProjectsController < ApplicationController
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds,
) )
end end
......
...@@ -126,7 +126,7 @@ module ProjectsHelper ...@@ -126,7 +126,7 @@ module ProjectsHelper
nav_tabs << :merge_requests nav_tabs << :merge_requests
end end
if project.builds_enabled? && can?(current_user, :read_build, project) if can?(current_user, :read_build, project)
nav_tabs << :builds nav_tabs << :builds
end end
......
...@@ -5,17 +5,18 @@ class Ability ...@@ -5,17 +5,18 @@ class Ability
return [] unless user.is_a?(User) return [] unless user.is_a?(User)
return [] if user.blocked? return [] if user.blocked?
case subject.class.name case subject
when "Project" then project_abilities(user, subject) when CommitStatus then commit_status_abilities(user, subject)
when "Issue" then issue_abilities(user, subject) when Project then project_abilities(user, subject)
when "Note" then note_abilities(user, subject) when Issue then issue_abilities(user, subject)
when "ProjectSnippet" then project_snippet_abilities(user, subject) when Note then note_abilities(user, subject)
when "PersonalSnippet" then personal_snippet_abilities(user, subject) when ProjectSnippet then project_snippet_abilities(user, subject)
when "MergeRequest" then merge_request_abilities(user, subject) when PersonalSnippet then personal_snippet_abilities(user, subject)
when "Group" then group_abilities(user, subject) when MergeRequest then merge_request_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject) when Group then group_abilities(user, subject)
when "GroupMember" then group_member_abilities(user, subject) when Namespace then namespace_abilities(user, subject)
when "ProjectMember" then project_member_abilities(user, subject) when GroupMember then group_member_abilities(user, subject)
when ProjectMember then project_member_abilities(user, subject)
else [] else []
end.concat(global_abilities(user)) end.concat(global_abilities(user))
end end
...@@ -25,6 +26,8 @@ class Ability ...@@ -25,6 +26,8 @@ class Ability
case true case true
when subject.is_a?(PersonalSnippet) when subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject) anonymous_personal_snippet_abilities(subject)
when subject.is_a?(CommitStatus)
anonymous_commit_status_abilities(subject)
when subject.is_a?(Project) || subject.respond_to?(:project) when subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject) anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group) when subject.is_a?(Group) || subject.respond_to?(:group)
...@@ -52,16 +55,26 @@ class Ability ...@@ -52,16 +55,26 @@ class Ability
:read_project_member, :read_project_member,
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_build, :read_commit_status,
:download_code :download_code
] ]
# Allow to read builds by anonymous user if guests are allowed
rules << :read_build if project.public_builds?
rules - project_disabled_features_rules(project) rules - project_disabled_features_rules(project)
else else
[] []
end end
end end
def anonymous_commit_status_abilities(subject)
rules = anonymous_project_abilities(subject.project)
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
rules
end
def anonymous_group_abilities(subject) def anonymous_group_abilities(subject)
group = if subject.is_a?(Group) group = if subject.is_a?(Group)
subject subject
...@@ -113,6 +126,9 @@ class Ability ...@@ -113,6 +126,9 @@ class Ability
if project.public? || project.internal? if project.public? || project.internal?
rules.push(*public_project_rules) rules.push(*public_project_rules)
# Allow to read builds for internal projects
rules << :read_build if project.public_builds?
end end
if project.owner == user || user.admin? if project.owner == user || user.admin?
...@@ -134,7 +150,8 @@ class Ability ...@@ -134,7 +150,8 @@ class Ability
def public_project_rules def public_project_rules
@public_project_rules ||= project_guest_rules + [ @public_project_rules ||= project_guest_rules + [
:download_code, :download_code,
:fork_project :fork_project,
:read_commit_status,
] ]
end end
...@@ -149,7 +166,6 @@ class Ability ...@@ -149,7 +166,6 @@ class Ability
:read_project_member, :read_project_member,
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_build,
:create_project, :create_project,
:create_issue, :create_issue,
:create_note :create_note
...@@ -158,24 +174,26 @@ class Ability ...@@ -158,24 +174,26 @@ class Ability
def project_report_rules def project_report_rules
@project_report_rules ||= project_guest_rules + [ @project_report_rules ||= project_guest_rules + [
:create_commit_status,
:read_commit_statuses,
:read_build_artifacts,
:download_code, :download_code,
:fork_project, :fork_project,
:create_project_snippet, :create_project_snippet,
:update_issue, :update_issue,
:admin_issue, :admin_issue,
:admin_label :admin_label,
:read_commit_status,
:read_build,
] ]
end end
def project_dev_rules def project_dev_rules
@project_dev_rules ||= project_report_rules + [ @project_dev_rules ||= project_report_rules + [
:admin_merge_request, :admin_merge_request,
:create_commit_status,
:update_commit_status,
:create_build,
:update_build,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:manage_builds,
:push_code :push_code
] ]
end end
...@@ -201,7 +219,9 @@ class Ability ...@@ -201,7 +219,9 @@ class Ability
:admin_merge_request, :admin_merge_request,
:admin_note, :admin_note,
:admin_wiki, :admin_wiki,
:admin_project :admin_project,
:admin_commit_status,
:admin_build
] ]
end end
...@@ -240,6 +260,10 @@ class Ability ...@@ -240,6 +260,10 @@ class Ability
rules += named_abilities('wiki') rules += named_abilities('wiki')
end end
unless project.builds_enabled
rules += named_abilities('build')
end
rules rules
end end
...@@ -376,6 +400,22 @@ class Ability ...@@ -376,6 +400,22 @@ class Ability
rules rules
end end
def commit_status_abilities(user, subject)
rules = project_abilities(user, subject.project)
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
rules
end
def filter_build_abilities(rules)
# If we can't read build we should also not have that
# ability when looking at this in context of commit_status
%w(read create update admin).each do |rule|
rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
end
rules
end
def abilities def abilities
@abilities ||= begin @abilities ||= begin
abilities = Six.new abilities = Six.new
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= ci_status_with_icon(build.status) = ci_status_with_icon(build.status)
%td.build-link %td.build-link
- if build.target_url - if can?(current_user, :read_build, project) && build.target_url
= link_to build.target_url do = link_to build.target_url do
%strong Build ##{build.id} %strong Build ##{build.id}
- else - else
...@@ -60,10 +60,10 @@ ...@@ -60,10 +60,10 @@
%td %td
.pull-right .pull-right
- if current_user && can?(current_user, :read_build_artifacts, project) && build.artifacts? - if can?(current_user, :read_build, project) && build.artifacts?
= link_to build.artifacts_download_url, title: 'Download artifacts' do = link_to build.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download %i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, build.project) - if can?(current_user, :update_build, build.project)
- if build.active? - if build.active?
- if build.cancel_url - if build.cancel_url
= link_to build.cancel_url, method: :post, title: 'Cancel' do = link_to build.cancel_url, method: :post, title: 'Cancel' do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.project-issuable-filter .project-issuable-filter
.controls .controls
- if can?(current_user, :manage_builds, @project) - if can?(current_user, :update_build, @project)
.pull-left.hidden-xs .pull-left.hidden-xs
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
= link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
......
...@@ -89,8 +89,7 @@ ...@@ -89,8 +89,7 @@
Test coverage Test coverage
%h1 #{@build.coverage}% %h1 #{@build.coverage}%
- if current_user && can?(current_user, :read_build_artifacts, @project) && @build.artifacts? - if can?(current_user, :read_build, @project) && @build.artifacts?
.build-widget.artifacts .build-widget.artifacts
%h4.title Build artifacts %h4.title Build artifacts
.center .center
...@@ -102,7 +101,7 @@ ...@@ -102,7 +101,7 @@
.build-widget .build-widget
%h4.title %h4.title
Build ##{@build.id} Build ##{@build.id}
- if current_user && can?(current_user, :manage_builds, @project) - if can?(current_user, :update_build, @project)
.pull-right .pull-right
- if @build.cancel_url - if @build.cancel_url
= link_to "Cancel", @build.cancel_url, class: 'btn btn-sm btn-danger', method: :post = link_to "Cancel", @build.cancel_url, class: 'btn btn-sm btn-danger', method: :post
......
.gray-content-block.middle-block .gray-content-block.middle-block
.pull-right .pull-right
- if can?(current_user, :manage_builds, @ci_commit.project) - if can?(current_user, :update_build, @ci_commit.project)
- if @ci_commit.builds.latest.failed.any?(&:retryable?) - if @ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
......
%tr.commit_status %tr.commit_status
%td.status %td.status
- if commit_status.target_url - if can?(current_user, :read_commit_status, commit_status) && commit_status.target_url
= link_to commit_status.target_url, class: "ci-status ci-#{commit_status.status}" do = link_to commit_status.target_url, class: "ci-status ci-#{commit_status.status}" do
= ci_icon_for_status(commit_status.status) = ci_icon_for_status(commit_status.status)
= commit_status.status = commit_status.status
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= ci_status_with_icon(commit_status.status) = ci_status_with_icon(commit_status.status)
%td.commit_status-link %td.commit_status-link
- if commit_status.target_url - if can?(current_user, :read_commit_status, commit_status) && commit_status.target_url
= link_to commit_status.target_url do = link_to commit_status.target_url do
%strong ##{commit_status.id} %strong ##{commit_status.id}
- else - else
...@@ -66,10 +66,10 @@ ...@@ -66,10 +66,10 @@
%td %td
.pull-right .pull-right
- if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts_download_url - if can?(current_user, :read_commit_status, commit_status) && commit_status.artifacts_download_url
= link_to commit_status.artifacts_download_url, title: 'Download artifacts' do = link_to commit_status.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download %i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, commit_status.project) - if can?(current_user, :update_commit_status, commit_status)
- if commit_status.active? - if commit_status.active?
- if commit_status.cancel_url - if commit_status.cancel_url
= link_to commit_status.cancel_url, method: :post, title: 'Cancel' do = link_to commit_status.cancel_url, method: :post, title: 'Cancel' do
......
...@@ -130,6 +130,7 @@ ...@@ -130,6 +130,7 @@
%strong git fetch %strong git fetch
%br %br
%span.descr Faster %span.descr Faster
.form-group .form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -158,6 +159,13 @@ ...@@ -158,6 +159,13 @@
phpunit --coverage-text --colors=never (PHP) - phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\% %code ^\s*Lines:\s*\d+.\d+\%
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public builds
.help-block Allow everyone to access builds for Public and Internal projects
%fieldset.features %fieldset.features
%legend %legend
......
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
Secret Variables Secret Variables
%p.light %p.light
These variables will be set to environment by the runner and will be hidden in the build log. These variables will be set to environment by the runner.
%br %br
So you can use them for passwords, secret keys or whatever you want. So you can use them for passwords, secret keys or whatever you want.
%br
The value of the variable can be visible in build log if explicitly asked to do so.
%hr %hr
......
...@@ -31,7 +31,7 @@ module Gitlab ...@@ -31,7 +31,7 @@ module Gitlab
config.encoding = "utf-8" config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt) config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt, :variables)
# Enable escaping HTML in JSON. # Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true config.active_support.escape_html_entities_in_json = true
......
class AddAllowGuestToAccessBuildsProject < ActiveRecord::Migration
def change
add_column :projects, :public_builds, :boolean, default: true, null: false
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160128233227) do ActiveRecord::Schema.define(version: 20160202164642) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -679,6 +679,7 @@ ActiveRecord::Schema.define(version: 20160128233227) do ...@@ -679,6 +679,7 @@ ActiveRecord::Schema.define(version: 20160128233227) do
t.string "build_coverage_regex" t.string "build_coverage_regex"
t.boolean "build_allow_git_fetch", default: true, null: false t.boolean "build_allow_git_fetch", default: true, null: false
t.integer "build_timeout", default: 3600, null: false t.integer "build_timeout", default: 3600, null: false
t.boolean "public_builds", default: true, null: false
end end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
......
...@@ -79,7 +79,9 @@ Parameters: ...@@ -79,7 +79,9 @@ Parameters:
"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0 "star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true
}, },
{ {
"id": 6, "id": 6,
...@@ -136,7 +138,8 @@ Parameters: ...@@ -136,7 +138,8 @@ Parameters:
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02" "runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true
} }
] ]
``` ```
...@@ -420,6 +423,7 @@ Parameters: ...@@ -420,6 +423,7 @@ Parameters:
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional)
### Create project for user ### Create project for user
...@@ -442,6 +446,7 @@ Parameters: ...@@ -442,6 +446,7 @@ Parameters:
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional)
### Edit project ### Edit project
...@@ -465,6 +470,7 @@ Parameters: ...@@ -465,6 +470,7 @@ Parameters:
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional)
On success, method returns 200 with the updated project. If parameters are On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned. invalid, 400 is returned.
......
...@@ -77,9 +77,12 @@ More information about Docker integration can be found in [Using Docker Images]( ...@@ -77,9 +77,12 @@ More information about Docker integration can be found in [Using Docker Images](
GitLab CI allows you to define per-project **Secure Variables** that are set in build environment. GitLab CI allows you to define per-project **Secure Variables** that are set in build environment.
The secure variables are stored out of the repository (the `.gitlab-ci.yml`). The secure variables are stored out of the repository (the `.gitlab-ci.yml`).
These variables are securely stored in GitLab CI database and are hidden in the build log. The variables are securely passed to GitLab Runner and are available in build environment.
It's desired method to use them for storing passwords, secret keys or whatever you want. It's desired method to use them for storing passwords, secret keys or whatever you want.
**The value of the variable can be shown in build log if explicitly asked to do so.**
If your project is public or internal you can make the builds private.
Secure Variables can added by going to `Project > Variables > Add Variable`. Secure Variables can added by going to `Project > Variables > Add Variable`.
They will be available for all subsequent builds. They will be available for all subsequent builds.
......
...@@ -18,6 +18,9 @@ documentation](../workflow/add-user/add-user.md). ...@@ -18,6 +18,9 @@ documentation](../workflow/add-user/add-user.md).
|---------------------------------------|---------|------------|-------------|----------|--------| |---------------------------------------|---------|------------|-------------|----------|--------|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ | | Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | | ✓ | ✓ | ✓ | ✓ | | Pull project code | | ✓ | ✓ | ✓ | ✓ |
| Download project | | ✓ | ✓ | ✓ | ✓ | | Download project | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ |
...@@ -31,6 +34,7 @@ documentation](../workflow/add-user/add-user.md). ...@@ -31,6 +34,7 @@ documentation](../workflow/add-user/add-user.md).
| Remove non-protected branches | | | ✓ | ✓ | ✓ | | Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ | | Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ | | Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ | | Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ | | Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ | | Push to protected branches | | | | ✓ | ✓ |
...@@ -40,12 +44,17 @@ documentation](../workflow/add-user/add-user.md). ...@@ -40,12 +44,17 @@ documentation](../workflow/add-user/add-user.md).
| Edit project | | | | ✓ | ✓ | | Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ | | Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ | | Configure project hooks | | | | ✓ | ✓ |
| Manage runners | | | | ✓ | ✓ |
| Manage build triggers | | | | ✓ | ✓ |
| Manage variables | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ | | Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ | | Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ | | Remove project | | | | | ✓ |
| Force push to protected branches | | | | | | | Force push to protected branches | | | | | |
| Remove protected branches | | | | | | | Remove protected branches | | | | | |
[^1]: If **Allow guest to access builds** is enabled in CI settings
## Group ## Group
In order for a group to appear as public and be browsable, it must contain at In order for a group to appear as public and be browsable, it must contain at
......
...@@ -5,6 +5,41 @@ Feature: Project Builds Permissions ...@@ -5,6 +5,41 @@ Feature: Project Builds Permissions
And project has CI enabled And project has CI enabled
And project has a recent build And project has a recent build
Scenario: I try to visit build details as guest
Given I am member of a project with a guest role
When I visit recent build summary page
Then page status code should be 404
Scenario: I try to visit project builds page as guest
Given I am member of a project with a guest role
When I visit project builds page
Then page status code should be 404
Scenario: I try to visit build details of internal project without access to builds
Given The project is internal
And public access for builds is disabled
When I visit recent build summary page
Then page status code should be 404
Scenario: I try to visit internal project builds page without access to builds
Given The project is internal
And public access for builds is disabled
When I visit project builds page
Then page status code should be 404
Scenario: I try to visit build details of internal project with access to builds
Given The project is internal
And public access for builds is enabled
When I visit recent build summary page
Then I see details of a build
And I see build trace
Scenario: I try to visit internal project builds page with access to builds
Given The project is internal
And public access for builds is enabled
When I visit project builds page
Then I see the build
Scenario: I try to download build artifacts as guest Scenario: I try to download build artifacts as guest
Given I am member of a project with a guest role Given I am member of a project with a guest role
And recent build has artifacts available And recent build has artifacts available
......
...@@ -14,6 +14,10 @@ module SharedBuilds ...@@ -14,6 +14,10 @@ module SharedBuilds
visit namespace_project_build_path(@project.namespace, @project, @build) visit namespace_project_build_path(@project.namespace, @project, @build)
end end
step 'I visit project builds page' do
visit namespace_project_builds_path(@project.namespace, @project)
end
step 'recent build has artifacts available' do step 'recent build has artifacts available' do
artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip' artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
archive = fixture_file_upload(artifacts, 'application/zip') archive = fixture_file_upload(artifacts, 'application/zip')
...@@ -34,4 +38,21 @@ module SharedBuilds ...@@ -34,4 +38,21 @@ module SharedBuilds
step 'I access artifacts download page' do step 'I access artifacts download page' do
visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build) visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build)
end end
step 'I see details of a build' do
expect(page).to have_content "Build ##{@build.id}"
end
step 'I see build trace' do
expect(page).to have_css '#build-trace'
end
step 'I see the build' do
page.within('.commit_status') do
expect(page).to have_content "##{@build.id}"
expect(page).to have_content @build.sha[0..7]
expect(page).to have_content @build.ref
expect(page).to have_content @build.name
end
end
end end
...@@ -240,6 +240,18 @@ module SharedProject ...@@ -240,6 +240,18 @@ module SharedProject
end end
end end
step 'The project is internal' do
@project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
step 'public access for builds is enabled' do
@project.update(public_builds: true)
end
step 'public access for builds is disabled' do
@project.update(public_builds: false)
end
def user_owns_project(user_name:, project_name:, visibility: :private) def user_owns_project(user_name:, project_name:, visibility: :private)
user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
project = Project.find_by(name: project_name) project = Project.find_by(name: project_name)
......
...@@ -13,11 +13,12 @@ module API ...@@ -13,11 +13,12 @@ module API
# Example Request: # Example Request:
# GET /projects/:id/builds # GET /projects/:id/builds
get ':id/builds' do get ':id/builds' do
builds = user_project.builds.order('id DESC') builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope]) builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build, present paginate(builds), with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
# Get builds for a specific commit of a project # Get builds for a specific commit of a project
...@@ -30,6 +31,8 @@ module API ...@@ -30,6 +31,8 @@ module API
# Example Request: # Example Request:
# GET /projects/:id/repository/commits/:sha/builds # GET /projects/:id/repository/commits/:sha/builds
get ':id/repository/commits/:sha/builds' do get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
commit = user_project.ci_commits.find_by_sha(params[:sha]) commit = user_project.ci_commits.find_by_sha(params[:sha])
return not_found! unless commit return not_found! unless commit
...@@ -37,7 +40,7 @@ module API ...@@ -37,7 +40,7 @@ module API
builds = filter_builds(builds, params[:scope]) builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build, present paginate(builds), with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
# Get a specific build of a project # Get a specific build of a project
...@@ -48,11 +51,13 @@ module API ...@@ -48,11 +51,13 @@ module API
# Example Request: # Example Request:
# GET /projects/:id/builds/:build_id # GET /projects/:id/builds/:build_id
get ':id/builds/:build_id' do get ':id/builds/:build_id' do
authorize_read_builds!
build = get_build(params[:build_id]) build = get_build(params[:build_id])
return not_found!(build) unless build return not_found!(build) unless build
present build, with: Entities::Build, present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
# Get a trace of a specific build of a project # Get a trace of a specific build of a project
...@@ -67,6 +72,8 @@ module API ...@@ -67,6 +72,8 @@ module API
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of # is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
get ':id/builds/:build_id/trace' do get ':id/builds/:build_id/trace' do
authorize_read_builds!
build = get_build(params[:build_id]) build = get_build(params[:build_id])
return not_found!(build) unless build return not_found!(build) unless build
...@@ -86,7 +93,7 @@ module API ...@@ -86,7 +93,7 @@ module API
# example request: # example request:
# post /projects/:id/build/:build_id/cancel # post /projects/:id/build/:build_id/cancel
post ':id/builds/:build_id/cancel' do post ':id/builds/:build_id/cancel' do
authorize_manage_builds! authorize_update_builds!
build = get_build(params[:build_id]) build = get_build(params[:build_id])
return not_found!(build) unless build return not_found!(build) unless build
...@@ -94,7 +101,7 @@ module API ...@@ -94,7 +101,7 @@ module API
build.cancel build.cancel
present build, with: Entities::Build, present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
# Retry a specific build of a project # Retry a specific build of a project
...@@ -105,7 +112,7 @@ module API ...@@ -105,7 +112,7 @@ module API
# example request: # example request:
# post /projects/:id/build/:build_id/retry # post /projects/:id/build/:build_id/retry
post ':id/builds/:build_id/retry' do post ':id/builds/:build_id/retry' do
authorize_manage_builds! authorize_update_builds!
build = get_build(params[:build_id]) build = get_build(params[:build_id])
return forbidden!('Build is not retryable') unless build && build.retryable? return forbidden!('Build is not retryable') unless build && build.retryable?
...@@ -113,7 +120,7 @@ module API ...@@ -113,7 +120,7 @@ module API
build = Ci::Build.retry(build) build = Ci::Build.retry(build)
present build, with: Entities::Build, present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
end end
...@@ -141,8 +148,12 @@ module API ...@@ -141,8 +148,12 @@ module API
builds.where(status: available_statuses && scope) builds.where(status: available_statuses && scope)
end end
def authorize_manage_builds! def authorize_read_builds!
authorize! :manage_builds, user_project authorize! :read_build, user_project
end
def authorize_update_builds!
authorize! :update_build, user_project
end end
end end
end end
......
...@@ -18,7 +18,7 @@ module API ...@@ -18,7 +18,7 @@ module API
# Examples: # Examples:
# GET /projects/:id/repository/commits/:sha/statuses # GET /projects/:id/repository/commits/:sha/statuses
get ':id/repository/commits/:sha/statuses' do get ':id/repository/commits/:sha/statuses' do
authorize! :read_commit_statuses, user_project authorize! :read_commit_status, user_project
sha = params[:sha] sha = params[:sha]
ci_commit = user_project.ci_commit(sha) ci_commit = user_project.ci_commit(sha)
not_found! 'Commit' unless ci_commit not_found! 'Commit' unless ci_commit
......
...@@ -72,6 +72,7 @@ module API ...@@ -72,6 +72,7 @@ module API
expose :star_count, :forks_count expose :star_count, :forks_count
expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? } expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds
end end
class ProjectMember < UserBasic class ProjectMember < UserBasic
...@@ -383,7 +384,7 @@ module API ...@@ -383,7 +384,7 @@ module API
# for downloading of artifacts (see: https://gitlab.com/gitlab-org/gitlab-ce/issues/4255) # for downloading of artifacts (see: https://gitlab.com/gitlab-org/gitlab-ce/issues/4255)
expose :download_url do |repo_obj, options| expose :download_url do |repo_obj, options|
if options[:user_can_download_artifacts] if options[:user_can_download_artifacts]
repo_obj.download_url repo_obj.artifacts_download_url
end end
end end
expose :commit, with: RepoCommit do |repo_obj, _options| expose :commit, with: RepoCommit do |repo_obj, _options|
......
...@@ -99,6 +99,7 @@ module API ...@@ -99,6 +99,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - 0 by default # visibility_level (optional) - 0 by default
# import_url (optional) # import_url (optional)
# public_builds (optional)
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
...@@ -115,7 +116,8 @@ module API ...@@ -115,7 +116,8 @@ module API
:namespace_id, :namespace_id,
:public, :public,
:visibility_level, :visibility_level,
:import_url] :import_url,
:public_builds]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute @project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved? if @project.saved?
...@@ -145,6 +147,7 @@ module API ...@@ -145,6 +147,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) # visibility_level (optional)
# import_url (optional) # import_url (optional)
# public_builds (optional)
# Example Request # Example Request
# POST /projects/user/:user_id # POST /projects/user/:user_id
post "user/:user_id" do post "user/:user_id" do
...@@ -161,7 +164,8 @@ module API ...@@ -161,7 +164,8 @@ module API
:shared_runners_enabled, :shared_runners_enabled,
:public, :public,
:visibility_level, :visibility_level,
:import_url] :import_url,
:public_builds]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute @project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved? if @project.saved?
...@@ -205,6 +209,7 @@ module API ...@@ -205,6 +209,7 @@ module API
# shared_runners_enabled (optional) # shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project # visibility_level (optional) - visibility level of a project
# public_builds (optional)
# Example Request # Example Request
# PUT /projects/:id # PUT /projects/:id
put ':id' do put ':id' do
...@@ -219,7 +224,8 @@ module API ...@@ -219,7 +224,8 @@ module API
:snippets_enabled, :snippets_enabled,
:shared_runners_enabled, :shared_runners_enabled,
:public, :public,
:visibility_level] :visibility_level,
:public_builds]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
authorize_admin_project authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present? authorize! :rename_project, user_project if attrs[:name].present?
......
...@@ -54,7 +54,7 @@ module API ...@@ -54,7 +54,7 @@ module API
# GET /projects/:id/triggers # GET /projects/:id/triggers
get ':id/triggers' do get ':id/triggers' do
authenticate! authenticate!
authorize_admin_project authorize! :admin_build, user_project
triggers = user_project.triggers.includes(:trigger_requests) triggers = user_project.triggers.includes(:trigger_requests)
triggers = paginate(triggers) triggers = paginate(triggers)
...@@ -71,7 +71,7 @@ module API ...@@ -71,7 +71,7 @@ module API
# GET /projects/:id/triggers/:token # GET /projects/:id/triggers/:token
get ':id/triggers/:token' do get ':id/triggers/:token' do
authenticate! authenticate!
authorize_admin_project authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s) trigger = user_project.triggers.find_by(token: params[:token].to_s)
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
...@@ -87,7 +87,7 @@ module API ...@@ -87,7 +87,7 @@ module API
# POST /projects/:id/triggers # POST /projects/:id/triggers
post ':id/triggers' do post ':id/triggers' do
authenticate! authenticate!
authorize_admin_project authorize! :admin_build, user_project
trigger = user_project.triggers.create trigger = user_project.triggers.create
...@@ -103,7 +103,7 @@ module API ...@@ -103,7 +103,7 @@ module API
# DELETE /projects/:id/triggers/:token # DELETE /projects/:id/triggers/:token
delete ':id/triggers/:token' do delete ':id/triggers/:token' do
authenticate! authenticate!
authorize_admin_project authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s) trigger = user_project.triggers.find_by(token: params[:token].to_s)
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
......
...@@ -2,7 +2,7 @@ module API ...@@ -2,7 +2,7 @@ module API
# Projects variables API # Projects variables API
class Variables < Grape::API class Variables < Grape::API
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize! :admin_build, user_project }
resource :projects do resource :projects do
# Get project variables # Get project variables
......
module Backup module Backup
class Manager class Manager
def pack def pack
# Make sure there is a connection
ActiveRecord::Base.connection.reconnect!
# saving additional informations # saving additional informations
s = {} s = {}
s[:db_version] = "#{ActiveRecord::Migrator.current_version}" s[:db_version] = "#{ActiveRecord::Migrator.current_version}"
......
...@@ -8,7 +8,7 @@ describe "Builds" do ...@@ -8,7 +8,7 @@ describe "Builds" do
@commit = FactoryGirl.create :ci_commit @commit = FactoryGirl.create :ci_commit
@build = FactoryGirl.create :ci_build, commit: @commit @build = FactoryGirl.create :ci_build, commit: @commit
@project = @commit.project @project = @commit.project
@project.team << [@user, :master] @project.team << [@user, :developer]
end end
describe "GET /:project/builds" do describe "GET /:project/builds" do
......
...@@ -8,7 +8,6 @@ describe 'Commits' do ...@@ -8,7 +8,6 @@ describe 'Commits' do
describe 'CI' do describe 'CI' do
before do before do
login_as :user login_as :user
project.team << [@user, :master]
stub_ci_commit_to_return_yaml_file stub_ci_commit_to_return_yaml_file
end end
...@@ -19,6 +18,10 @@ describe 'Commits' do ...@@ -19,6 +18,10 @@ describe 'Commits' do
context 'commit status is Generic Commit Status' do context 'commit status is Generic Commit Status' do
let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit } let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit }
before do
project.team << [@user, :reporter]
end
describe 'Commit builds' do describe 'Commit builds' do
before do before do
visit ci_status_path(commit) visit ci_status_path(commit)
...@@ -37,6 +40,12 @@ describe 'Commits' do ...@@ -37,6 +40,12 @@ describe 'Commits' do
context 'commit status is Ci Build' do context 'commit status is Ci Build' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit } let!(:build) { FactoryGirl.create :ci_build, commit: commit }
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
context 'when logged as developer' do
before do
project.team << [@user, :developer]
end
describe 'Project commits' do describe 'Project commits' do
before do before do
...@@ -61,8 +70,6 @@ describe 'Commits' do ...@@ -61,8 +70,6 @@ describe 'Commits' do
end end
context 'Download artifacts' do context 'Download artifacts' do
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
before do before do
build.update_attributes(artifacts_file: artifacts_file) build.update_attributes(artifacts_file: artifacts_file)
end end
...@@ -117,5 +124,42 @@ describe 'Commits' do ...@@ -117,5 +124,42 @@ describe 'Commits' do
end end
end end
end end
context "when logged as reporter" do
before do
project.team << [@user, :reporter]
build.update_attributes(artifacts_file: artifacts_file)
visit ci_status_path(commit)
end
it do
expect(page).to have_content commit.sha[0..7]
expect(page).to have_content commit.git_commit_message
expect(page).to have_content commit.git_author_name
expect(page).to have_link('Download artifacts')
expect(page).to_not have_link('Cancel running')
expect(page).to_not have_link('Retry failed')
end
end
context 'when accessing internal project with disallowed access' do
before do
project.update(
visibility_level: Gitlab::VisibilityLevel::INTERNAL,
public_builds: false)
build.update_attributes(artifacts_file: artifacts_file)
visit ci_status_path(commit)
end
it do
expect(page).to have_content commit.sha[0..7]
expect(page).to have_content commit.git_commit_message
expect(page).to have_content commit.git_author_name
expect(page).to_not have_link('Download artifacts')
expect(page).to_not have_link('Cancel running')
expect(page).to_not have_link('Retry failed')
end
end
end
end end
end end
...@@ -96,6 +96,60 @@ describe "Public Project Access", feature: true do ...@@ -96,6 +96,60 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/builds" do
subject { namespace_project_builds_path(project.namespace, project) }
context "when allowed for public" do
before { project.update(public_builds: true) }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe "GET /:project_path/builds/:id" do
let(:commit) { create(:ci_commit, project: project) }
let(:build) { create(:ci_build, commit: commit) }
subject { namespace_project_build_path(project.namespace, project, build.id) }
context "when allowed for public" do
before { project.update(public_builds: true) }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe "GET /:project_path/blob" do describe "GET /:project_path/blob" do
before do before do
commit = project.repository.commit commit = project.repository.commit
......
#= require behaviors/autosize
describe 'Autosize behavior', ->
beforeEach ->
fixture.set('<textarea class="js-autosize" style="resize: vertical"></textarea>')
it 'does not overwrite the resize property', ->
load()
expect($('textarea')).toHaveCss(resize: 'vertical')
load = -> $(document).trigger('page:load')
...@@ -113,7 +113,7 @@ describe API::API, api: true do ...@@ -113,7 +113,7 @@ describe API::API, api: true do
describe 'POST /projects/:id/builds/:build_id/cancel' do describe 'POST /projects/:id/builds/:build_id/cancel' do
context 'authorized user' do context 'authorized user' do
context 'user with :manage_builds persmission' do context 'user with :update_build persmission' do
it 'should cancel running or pending build' do it 'should cancel running or pending build' do
post api("/projects/#{project.id}/builds/#{build.id}/cancel", user) post api("/projects/#{project.id}/builds/#{build.id}/cancel", user)
...@@ -122,7 +122,7 @@ describe API::API, api: true do ...@@ -122,7 +122,7 @@ describe API::API, api: true do
end end
end end
context 'user without :manage_builds permission' do context 'user without :update_build permission' do
it 'should not cancel build' do it 'should not cancel build' do
post api("/projects/#{project.id}/builds/#{build.id}/cancel", user2) post api("/projects/#{project.id}/builds/#{build.id}/cancel", user2)
...@@ -142,7 +142,7 @@ describe API::API, api: true do ...@@ -142,7 +142,7 @@ describe API::API, api: true do
describe 'POST /projects/:id/builds/:build_id/retry' do describe 'POST /projects/:id/builds/:build_id/retry' do
context 'authorized user' do context 'authorized user' do
context 'user with :manage_builds persmission' do context 'user with :update_build persmission' do
it 'should retry non-running build' do it 'should retry non-running build' do
post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user) post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user)
...@@ -152,7 +152,7 @@ describe API::API, api: true do ...@@ -152,7 +152,7 @@ describe API::API, api: true do
end end
end end
context 'user without :manage_builds permission' do context 'user without :update_build permission' do
it 'should not retry build' do it 'should not retry build' do
post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user2) post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user2)
......
...@@ -2,18 +2,17 @@ require 'spec_helper' ...@@ -2,18 +2,17 @@ require 'spec_helper'
describe API::CommitStatus, api: true do describe API::CommitStatus, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let!(:project) { create(:project) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) }
let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
let!(:ci_commit) { project.ensure_ci_commit(commit.id) } let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
let(:commit_status) { create(:commit_status, commit: ci_commit) } let(:commit_status) { create(:commit_status, commit: ci_commit) }
let(:guest) { create_user(ProjectMember::GUEST) }
let(:reporter) { create_user(ProjectMember::REPORTER) }
let(:developer) { create_user(ProjectMember::DEVELOPER) }
describe "GET /projects/:id/repository/commits/:sha/statuses" do describe "GET /projects/:id/repository/commits/:sha/statuses" do
it_behaves_like 'a paginated resources' do it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) } let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) }
end end
context "reporter user" do context "reporter user" do
...@@ -29,7 +28,7 @@ describe API::CommitStatus, api: true do ...@@ -29,7 +28,7 @@ describe API::CommitStatus, api: true do
end end
it "should return latest commit statuses" do it "should return latest commit statuses" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
...@@ -39,7 +38,7 @@ describe API::CommitStatus, api: true do ...@@ -39,7 +38,7 @@ describe API::CommitStatus, api: true do
end end
it "should return all commit statuses" do it "should return all commit statuses" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user) get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", reporter)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
...@@ -47,7 +46,7 @@ describe API::CommitStatus, api: true do ...@@ -47,7 +46,7 @@ describe API::CommitStatus, api: true do
end end
it "should return latest commit statuses for specific ref" do it "should return latest commit statuses for specific ref" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user) get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", reporter)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
...@@ -55,7 +54,7 @@ describe API::CommitStatus, api: true do ...@@ -55,7 +54,7 @@ describe API::CommitStatus, api: true do
end end
it "should return latest commit statuses for specific name" do it "should return latest commit statuses for specific name" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user) get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", reporter)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
...@@ -65,7 +64,7 @@ describe API::CommitStatus, api: true do ...@@ -65,7 +64,7 @@ describe API::CommitStatus, api: true do
context "guest user" do context "guest user" do
it "should not return project commits" do it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2) get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", guest)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
...@@ -81,10 +80,10 @@ describe API::CommitStatus, api: true do ...@@ -81,10 +80,10 @@ describe API::CommitStatus, api: true do
describe 'POST /projects/:id/statuses/:sha' do describe 'POST /projects/:id/statuses/:sha' do
let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" }
context 'reporter user' do context 'developer user' do
context 'should create commit status' do context 'should create commit status' do
it 'with only required parameters' do it 'with only required parameters' do
post api(post_url, user), state: 'success' post api(post_url, developer), state: 'success'
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id) expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success') expect(json_response['status']).to eq('success')
...@@ -95,7 +94,7 @@ describe API::CommitStatus, api: true do ...@@ -95,7 +94,7 @@ describe API::CommitStatus, api: true do
end end
it 'with all optional parameters' do it 'with all optional parameters' do
post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' post api(post_url, developer), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test'
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id) expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success') expect(json_response['status']).to eq('success')
...@@ -108,25 +107,32 @@ describe API::CommitStatus, api: true do ...@@ -108,25 +107,32 @@ describe API::CommitStatus, api: true do
context 'should not create commit status' do context 'should not create commit status' do
it 'with invalid state' do it 'with invalid state' do
post api(post_url, user), state: 'invalid' post api(post_url, developer), state: 'invalid'
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
it 'without state' do it 'without state' do
post api(post_url, user) post api(post_url, developer)
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
it 'invalid commit' do it 'invalid commit' do
post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running' post api("/projects/#{project.id}/statuses/invalid_sha", developer), state: 'running'
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
end end
end end
context 'reporter user' do
it 'should not create commit status' do
post api(post_url, reporter)
expect(response.status).to eq(403)
end
end
context 'guest user' do context 'guest user' do
it 'should not create commit status' do it 'should not create commit status' do
post api(post_url, user2) post api(post_url, guest)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
...@@ -138,4 +144,10 @@ describe API::CommitStatus, api: true do ...@@ -138,4 +144,10 @@ describe API::CommitStatus, api: true do
end end
end end
end end
def create_user(access_level)
user = create(:user)
create(:project_member, user: user, project: project, access_level: access_level)
user
end
end end
/*!
* jQuery resize event - v1.1 - 3/14/2010
* http://benalman.com/projects/jquery-resize-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
// Script: jQuery resize event
//
// *Version: 1.1, Last updated: 3/14/2010*
//
// Project Home - http://benalman.com/projects/jquery-resize-plugin/
// GitHub - http://github.com/cowboy/jquery-resize/
// Source - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.js
// (Minified) - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.min.js (1.0kb)
//
// About: License
//
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// This working example, complete with fully commented code, illustrates a few
// ways in which this plugin can be used.
//
// resize event - http://benalman.com/code/projects/jquery-resize/examples/resize/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
//
// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1.
// Unit Tests - http://benalman.com/code/projects/jquery-resize/unit/
//
// About: Release History
//
// 1.1 - (3/14/2010) Fixed a minor bug that was causing the event to trigger
// immediately after bind in some circumstances. Also changed $.fn.data
// to $.data to improve performance.
// 1.0 - (2/10/2010) Initial release
(function($,window,undefined){
'$:nomunge'; // Used by YUI compressor.
// A jQuery object containing all non-window elements to which the resize
// event is bound.
var elems = $([]),
// Extend $.resize if it already exists, otherwise create it.
jq_resize = $.resize = $.extend( $.resize, {} ),
timeout_id,
// Reused strings.
str_setTimeout = 'setTimeout',
str_resize = 'resize',
str_data = str_resize + '-special-event',
str_delay = 'delay',
str_throttle = 'throttleWindow';
// Property: jQuery.resize.delay
//
// The numeric interval (in milliseconds) at which the resize event polling
// loop executes. Defaults to 250.
jq_resize[ str_delay ] = 250;
// Property: jQuery.resize.throttleWindow
//
// Throttle the native window object resize event to fire no more than once
// every <jQuery.resize.delay> milliseconds. Defaults to true.
//
// Because the window object has its own resize event, it doesn't need to be
// provided by this plugin, and its execution can be left entirely up to the
// browser. However, since certain browsers fire the resize event continuously
// while others do not, enabling this will throttle the window resize event,
// making event behavior consistent across all elements in all browsers.
//
// While setting this property to false will disable window object resize
// event throttling, please note that this property must be changed before any
// window object resize event callbacks are bound.
jq_resize[ str_throttle ] = true;
// Event: resize event
//
// Fired when an element's width or height changes. Because browsers only
// provide this event for the window element, for other elements a polling
// loop is initialized, running every <jQuery.resize.delay> milliseconds
// to see if elements' dimensions have changed. You may bind with either
// .resize( fn ) or .bind( "resize", fn ), and unbind with .unbind( "resize" ).
//
// Usage:
//
// > jQuery('selector').bind( 'resize', function(e) {
// > // element's width or height has changed!
// > ...
// > });
//
// Additional Notes:
//
// * The polling loop is not created until at least one callback is actually
// bound to the 'resize' event, and this single polling loop is shared
// across all elements.
//
// Double firing issue in jQuery 1.3.2:
//
// While this plugin works in jQuery 1.3.2, if an element's event callbacks
// are manually triggered via .trigger( 'resize' ) or .resize() those
// callbacks may double-fire, due to limitations in the jQuery 1.3.2 special
// events system. This is not an issue when using jQuery 1.4+.
//
// > // While this works in jQuery 1.4+
// > $(elem).css({ width: new_w, height: new_h }).resize();
// >
// > // In jQuery 1.3.2, you need to do this:
// > var elem = $(elem);
// > elem.css({ width: new_w, height: new_h });
// > elem.data( 'resize-special-event', { width: elem.width(), height: elem.height() } );
// > elem.resize();
$.event.special[ str_resize ] = {
// Called only when the first 'resize' event callback is bound per element.
setup: function() {
// Since window has its own native 'resize' event, return false so that
// jQuery will bind the event using DOM methods. Since only 'window'
// objects have a .setTimeout method, this should be a sufficient test.
// Unless, of course, we're throttling the 'resize' event for window.
if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
var elem = $(this);
// Add this element to the list of internal elements to monitor.
elems = elems.add( elem );
// Initialize data store on the element.
$.data( this, str_data, { w: elem.width(), h: elem.height() } );
// If this is the first element added, start the polling loop.
if ( elems.length === 1 ) {
loopy();
}
},
// Called only when the last 'resize' event callback is unbound per element.
teardown: function() {
// Since window has its own native 'resize' event, return false so that
// jQuery will unbind the event using DOM methods. Since only 'window'
// objects have a .setTimeout method, this should be a sufficient test.
// Unless, of course, we're throttling the 'resize' event for window.
if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
var elem = $(this);
// Remove this element from the list of internal elements to monitor.
elems = elems.not( elem );
// Remove any data stored on the element.
elem.removeData( str_data );
// If this is the last element removed, stop the polling loop.
if ( !elems.length ) {
clearTimeout( timeout_id );
}
},
// Called every time a 'resize' event callback is bound per element (new in
// jQuery 1.4).
add: function( handleObj ) {
// Since window has its own native 'resize' event, return false so that
// jQuery doesn't modify the event object. Unless, of course, we're
// throttling the 'resize' event for window.
if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
var old_handler;
// The new_handler function is executed every time the event is triggered.
// This is used to update the internal element data store with the width
// and height when the event is triggered manually, to avoid double-firing
// of the event callback. See the "Double firing issue in jQuery 1.3.2"
// comments above for more information.
function new_handler( e, w, h ) {
var elem = $(this),
data = $.data( this, str_data );
// If called from the polling loop, w and h will be passed in as
// arguments. If called manually, via .trigger( 'resize' ) or .resize(),
// those values will need to be computed.
data.w = w !== undefined ? w : elem.width();
data.h = h !== undefined ? h : elem.height();
old_handler.apply( this, arguments );
};
// This may seem a little complicated, but it normalizes the special event
// .add method between jQuery 1.4/1.4.1 and 1.4.2+
if ( $.isFunction( handleObj ) ) {
// 1.4, 1.4.1
old_handler = handleObj;
return new_handler;
} else {
// 1.4.2+
old_handler = handleObj.handler;
handleObj.handler = new_handler;
}
}
};
function loopy() {
// Start the polling loop, asynchronously.
timeout_id = window[ str_setTimeout ](function(){
// Iterate over all elements to which the 'resize' event is bound.
elems.each(function(){
var elem = $(this),
width = elem.width(),
height = elem.height(),
data = $.data( this, str_data );
// If element size has changed since the last time, update the element
// data store and trigger the 'resize' event.
if ( width !== data.w || height !== data.h ) {
elem.trigger( str_resize, [ data.w = width, data.h = height ] );
}
});
// Loop.
loopy();
}, jq_resize[ str_delay ] );
};
})(jQuery,this);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment