Commit cb261abe authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents 613bcdc6 8959de25
......@@ -2,8 +2,10 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased)
- Allow enabling wiki page events from Webhook management UI
- Fix wiki page events' webhook to point to the wiki repository
- Allow forking projects with restricted visibility level
- Improve note validation to prevent errors when creating invalid note via API
- Remove project notification settings associated with deleted projects
- Redesign navigation for project pages
- Fix groups API to list only user's accessible projects
- Redesign account and email confirmation emails
......@@ -17,6 +19,7 @@ v 8.9.0 (unreleased)
- Projects pending deletion will render a 404 page
- Measure queue duration between gitlab-workhorse and Rails
- Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min)
v 8.8.3
- Fix gitlab importer failing to import new projects due to missing credentials
......
......@@ -73,7 +73,7 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
gem "kaminari", "~> 0.16.3"
gem "kaminari", "~> 0.17.0"
# HAML
gem "haml-rails", '~> 0.9.0'
......
......@@ -442,7 +442,7 @@ GEM
railties (>= 3.2.16)
json (1.8.3)
jwt (1.5.2)
kaminari (0.16.3)
kaminari (0.17.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.10.0)
......@@ -959,7 +959,7 @@ DEPENDENCIES
jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.16.3)
kaminari (~> 0.17.0)
letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0)
loofah (~> 2.0.3)
......
......@@ -16,6 +16,19 @@
@include btn-default;
}
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
background-color: $background;
color: $text;
border-color: $border;
&:hover,
&:focus {
background-color: $hover-background;
color: $hover-text;
border-color: $hover-border;;
}
}
@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
background-color: $light;
border-color: $border-light;
......@@ -106,11 +119,14 @@
@include btn-blue;
}
&.btn-close,
&.btn-warning {
@include btn-orange;
}
&.btn-close {
@include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
}
&.btn-danger,
&.btn-remove,
&.btn-red {
......
......@@ -104,7 +104,7 @@ $blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
$orange-light: rgba(252, 109, 38, 0.80);
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
......
......@@ -107,6 +107,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:repository_checks_enabled,
:metrics_packet_size,
:send_user_confirmation_email,
:container_registry_token_expire_delay,
restricted_visibility_levels: [],
import_sources: [],
disabled_oauth_sign_in_sources: []
......
......@@ -51,6 +51,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :container_registry_token_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
......@@ -121,7 +125,8 @@ class ApplicationSetting < ActiveRecord::Base
akismet_enabled: false,
repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
)
end
......
......@@ -27,6 +27,10 @@ class ProjectWiki
@project.path_with_namespace + ".wiki"
end
def web_url
Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home)
end
def url_to_repo
gitlab_shell.url_to_repo(path_with_namespace)
end
......@@ -142,6 +146,16 @@ class ProjectWiki
wiki
end
def hook_attrs
{
web_url: web_url,
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
path_with_namespace: path_with_namespace,
default_branch: default_branch
}
end
private
def init_repo(path_with_namespace)
......
module Auth
class ContainerRegistryAuthenticationService < BaseService
include Gitlab::CurrentSettings
AUDIENCE = 'container_registry'
def execute
......@@ -17,6 +19,7 @@ module Auth
token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer
token.audience = AUDIENCE
token.expire_time = token_expire_at
token[:access] = names.map do |name|
{ type: 'repository', name: name, actions: %w(*) }
end
......@@ -30,6 +33,7 @@ module Auth
token.issuer = registry.issuer
token.audience = params[:service]
token.subject = current_user.try(:username)
token.expire_time = ContainerRegistryAuthenticationService.token_expire_at
token[:access] = accesses.compact
token
end
......@@ -75,5 +79,9 @@ module Auth
def registry
Gitlab.config.registry
end
def self.token_expire_at
Time.now + current_application_settings.container_registry_token_expire_delay.minutes
end
end
end
......@@ -6,9 +6,8 @@ module WikiPages
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
object_attributes: page.hook_attrs,
# DEPRECATED
repository: @project.hook_attrs.slice(:name, :url, :description, :homepage)
wiki: @project.wiki.hook_attrs,
object_attributes: page.hook_attrs
}
page_url = Gitlab::UrlBuilder.build(page)
......
......@@ -178,6 +178,14 @@
.col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control'
- if Gitlab.config.registry.enabled
%fieldset
%legend Container Registry
.form-group
= f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :container_registry_token_expire_delay, class: 'form-control'
%fieldset
%legend Metrics
%p
......
......@@ -4,7 +4,7 @@
= event_action_name(event)
- if event.target
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], title: event.target_title
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
= event_preposition(event)
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the first page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li.first
......
-# Non-link tag that stands for skipped pages...
-# available local variables
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li{class: "page"}
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the last page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li.last
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the next page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.last?
......
......@@ -3,7 +3,7 @@
-# page: a page object for "this" page
-# url: url to this page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li{class: "page#{' active' if page.current?}"}
......
-# The container tag
-# available local variables
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-# paginator: the paginator that renders the pagination tags inside
......@@ -9,7 +9,7 @@
%div.gl-pagination
%ul.pagination.clearfix
- unless current_page.first?
= first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages
= first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages
= prev_page_tag
- each_page do |page|
- if page.left_outer? || page.right_outer? || page.inside_window?
......@@ -18,5 +18,5 @@
= gap_tag
= next_page_tag
- unless current_page.last?
= last_page_tag unless num_pages < 5
= last_page_tag unless total_pages < 5
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the previous page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.first?
......
class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration
def up
execute <<-SQL
DELETE FROM notification_settings
WHERE notification_settings.source_type = 'Project'
AND NOT EXISTS (
SELECT *
FROM projects
WHERE projects.id = notification_settings.source_id
)
SQL
end
end
# This is ONLINE migration
class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column :application_settings, :container_registry_token_expire_delay, :integer, default: 5
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160528043124) do
ActiveRecord::Schema.define(version: 20160530150109) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -43,46 +43,47 @@ ActiveRecord::Schema.define(version: 20160528043124) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "home_page_url"
t.integer "default_branch_protection", default: 2
t.integer "default_branch_protection", default: 2
t.text "restricted_visibility_levels"
t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
t.boolean "user_oauth_applications", default: true
t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false
t.integer "session_expire_delay", default: 10080, null: false
t.text "import_sources"
t.text "help_page_text"
t.string "admin_notification_email"
t.boolean "shared_runners_enabled", default: true, null: false
t.integer "max_artifacts_size", default: 100, null: false
t.boolean "shared_runners_enabled", default: true, null: false
t.integer "max_artifacts_size", default: 100, null: false
t.string "runners_registration_token"
t.boolean "require_two_factor_authentication", default: false
t.integer "two_factor_grace_period", default: 48
t.boolean "metrics_enabled", default: false
t.string "metrics_host", default: "localhost"
t.integer "metrics_pool_size", default: 16
t.integer "metrics_timeout", default: 10
t.integer "metrics_method_call_threshold", default: 10
t.boolean "recaptcha_enabled", default: false
t.boolean "require_two_factor_authentication", default: false
t.integer "two_factor_grace_period", default: 48
t.boolean "metrics_enabled", default: false
t.string "metrics_host", default: "localhost"
t.integer "metrics_pool_size", default: 16
t.integer "metrics_timeout", default: 10
t.integer "metrics_method_call_threshold", default: 10
t.boolean "recaptcha_enabled", default: false
t.string "recaptcha_site_key"
t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089
t.boolean "akismet_enabled", default: false
t.integer "metrics_port", default: 8089
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false
t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false
t.string "sentry_dsn"
t.boolean "email_author_in_body", default: false
t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false
t.boolean "repository_checks_enabled", default: false
t.text "shared_runners_text"
t.integer "metrics_packet_size", default: 1
t.integer "metrics_packet_size", default: 1
t.text "disabled_oauth_sign_in_sources"
t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false
t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5
end
create_table "audit_events", force: :cascade do |t|
......
......@@ -37,7 +37,8 @@ Example response:
"created_at" : "2016-01-04T15:44:55.176Z",
"default_project_visibility" : 0,
"gravatar_enabled" : true,
"sign_in_text" : null
"sign_in_text" : null,
"container_registry_token_expire_delay": 5
}
```
......@@ -64,6 +65,7 @@ PUT /application/settings
| `restricted_signup_domains` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
| `after_sign_out_path` | string | no | Where to redirect users after logout |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
```bash
curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
......@@ -90,6 +92,7 @@ Example response:
"default_snippet_visibility": 0,
"restricted_signup_domains": [],
"user_oauth_applications": true,
"after_sign_out_path": ""
"after_sign_out_path": "",
"container_registry_token_expire_delay": 5
}
```
......@@ -720,7 +720,7 @@ X-Gitlab-Event: Wiki Page Hook
"description": "This is awesome",
"web_url": "http://example.com/root/awesome-project",
"avatar_url": null,
"git_ssh_url": "git@example.com:root/test-project.git",
"git_ssh_url": "git@example.com:root/awesome-project.git",
"git_http_url": "http://example.com/root/awesome-project.git",
"namespace": "root",
"visibility_level": 0,
......@@ -731,6 +731,13 @@ X-Gitlab-Event: Wiki Page Hook
"ssh_url": "git@example.com:root/awesome-project.git",
"http_url": "http://example.com/root/awesome-project.git"
},
"wiki": {
"web_url": "http://example.com/root/awesome-project/wikis/home",
"git_ssh_url": "git@example.com:root/awesome-project.wiki.git",
"git_http_url": "http://example.com/root/awesome-project.wiki.git",
"path_with_namespace": "root/awesome-project.wiki",
"default_branch": "master"
},
"object_attributes": {
"title": "Awesome",
"content": "awesome content goes here",
......@@ -739,12 +746,6 @@ X-Gitlab-Event: Wiki Page Hook
"slug": "awesome",
"url": "http://example.com/root/awesome-project/wikis/awesome",
"action": "create"
},
"repository": {
"name": "awesome-project",
"url": "git@example.com:root/awesome-project.git",
"description": "test",
"homepage": "http://example.com/root/awesome-project"
}
}
```
......
......@@ -362,6 +362,7 @@ module API
expose :restricted_signup_domains
expose :user_oauth_applications
expose :after_sign_out_path
expose :container_registry_token_expire_delay
end
class Release < Grape::Entity
......
......@@ -48,7 +48,7 @@ module Backup
end
connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.get(remote_directory)
directory = connection.directories.create(key: remote_directory)
if directory.files.create(key: tar_file, body: File.open(tar_file), public: false,
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
......
......@@ -36,6 +36,7 @@ module Gitlab
two_factor_grace_period: 48,
akismet_enabled: false,
repository_checks_enabled: true,
container_registry_token_expire_delay: 5,
)
end
......
require 'spec_helper'
describe Ci::Build, models: true do
let(:project) { FactoryGirl.create :project }
let(:commit) { FactoryGirl.create :ci_commit, project: project }
let(:build) { FactoryGirl.create :ci_build, commit: commit }
let(:project) { create(:project) }
let(:commit) { create(:ci_commit, project: project) }
let(:build) { create(:ci_build, commit: commit) }
it { is_expected.to validate_presence_of :ref }
it { is_expected.to respond_to :trace_html }
describe '#first_pending' do
let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday }
let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' }
before { first; second }
let!(:first) { create(:ci_build, commit: commit, status: 'pending', created_at: Date.yesterday) }
let!(:second) { create(:ci_build, commit: commit, status: 'pending') }
subject { Ci::Build.first_pending }
it { is_expected.to be_a(Ci::Build) }
......@@ -219,8 +218,8 @@ describe Ci::Build, models: true do
it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }
context 'and trigger variables' do
let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger }
let(:trigger) { create(:ci_trigger, project: project) }
let(:trigger_request) { create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) }
let(:trigger_variables) do
[
{ key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
......@@ -329,7 +328,7 @@ describe Ci::Build, models: true do
end
context 'if there are runner' do
let(:runner) { FactoryGirl.create :ci_runner }
let(:runner) { create(:ci_runner) }
before do
build.project.runners << runner
......@@ -366,7 +365,7 @@ describe Ci::Build, models: true do
it { is_expected.to be_truthy }
context "and there are specific runner" do
let(:runner) { FactoryGirl.create :ci_runner, contacted_at: 1.second.ago }
let(:runner) { create(:ci_runner, contacted_at: 1.second.ago) }
before do
build.project.runners << runner
......@@ -415,7 +414,7 @@ describe Ci::Build, models: true do
end
describe '#repo_url' do
let(:build) { FactoryGirl.create :ci_build }
let(:build) { create(:ci_build) }
let(:project) { build.project }
subject { build.repo_url }
......@@ -429,10 +428,10 @@ describe Ci::Build, models: true do
end
describe '#depends_on_builds' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit, name: 'build', stage_idx: 0, stage: 'build' }
let!(:rspec_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rspec', stage_idx: 1, stage: 'test' }
let!(:rubocop_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rubocop', stage_idx: 1, stage: 'test' }
let!(:staging) { FactoryGirl.create :ci_build, commit: commit, name: 'staging', stage_idx: 2, stage: 'deploy' }
let!(:build) { create(:ci_build, commit: commit, name: 'build', stage_idx: 0, stage: 'build') }
let!(:rspec_test) { create(:ci_build, commit: commit, name: 'rspec', stage_idx: 1, stage: 'test') }
let!(:rubocop_test) { create(:ci_build, commit: commit, name: 'rubocop', stage_idx: 1, stage: 'test') }
let!(:staging) { create(:ci_build, commit: commit, name: 'staging', stage_idx: 2, stage: 'deploy') }
it 'to have no dependents if this is first build' do
expect(build.depends_on_builds).to be_empty
......@@ -453,11 +452,10 @@ describe Ci::Build, models: true do
end
def create_mr(build, commit, factory: :merge_request, created_at: Time.now)
FactoryGirl.create(factory,
source_project_id: commit.gl_project_id,
target_project_id: commit.gl_project_id,
source_branch: build.ref,
created_at: created_at)
create(factory, source_project_id: commit.gl_project_id,
target_project_id: commit.gl_project_id,
source_branch: build.ref,
created_at: created_at)
end
describe '#merge_request' do
......@@ -501,8 +499,8 @@ describe Ci::Build, models: true do
context 'when a Build is created after the MR' do
before do
@merge_request = create_mr(build, commit, factory: :merge_request_with_diffs)
commit2 = FactoryGirl.create :ci_commit, project: project
@build2 = FactoryGirl.create :ci_build, commit: commit2
commit2 = create(:ci_commit, project: project)
@build2 = create(:ci_build, commit: commit2)
commits = [double(id: commit.sha), double(id: commit2.sha)]
allow(@merge_request).to receive(:commits).and_return(commits)
......
......@@ -16,6 +16,12 @@ describe ProjectWiki, models: true do
end
end
describe '#web_url' do
it 'returns the full web URL to the wiki' do
expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home")
end
end
describe "#url_to_repo" do
it "returns the correct ssh url to the repo" do
expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace))
......@@ -257,6 +263,13 @@ describe ProjectWiki, models: true do
end
end
describe '#hook_attrs' do
it 'returns a hash with values' do
expect(subject.hook_attrs).to be_a Hash
expect(subject.hook_attrs.keys).to contain_exactly(:web_url, :git_ssh_url, :git_http_url, :path_with_namespace, :default_branch)
end
end
private
def create_temp_repo(path)
......
......@@ -17,6 +17,21 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
shared_examples 'a valid token' do
it { is_expected.to include(:token) }
it { expect(payload).to include('access') }
context 'a expirable' do
let(:expires_at) { Time.at(payload['exp']) }
let(:expire_delay) { 10 }
context 'for default configuration' do
it { expect(expires_at).not_to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
end
context 'for changed configuration' do
before { stub_application_setting(container_registry_token_expire_delay: expire_delay) }
it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
end
end
end
shared_examples 'a accessible' do
......
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