diff options
45 files changed, 277 insertions, 147 deletions
diff --git a/CHANGELOG b/CHANGELOG index cc287b67063..ff41575bcc6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,7 +5,10 @@ v 7.5.0 - Fix LDAP config lookup for provider 'ldap' - Add Atlassian Bamboo CI service (Drew Blessing) - Mentioned @user will receive email even if he is not participating in issue or commit + - Session API: Use case-insensitive authentication like in UI (Andrey Krivko) - Tie up loose ends with annotated tags: API & UI (Sean Edge) + - Return valid json for deleting branch via API (sponsored by O'Reilly Media) + - Expose username in project events API (sponsored by O'Reilly Media) v 7.4.2 - Fix internal snippet exposing for unauthenticated users @@ -50,7 +53,7 @@ v 7.4.0 - Fix ambiguous sha problem with mentioned commit - Fixed bug with apostrophe when at mentioning users - Add active directory ldap option - - Developers can push to wiki repo. Protected branches does not affect wiki repo any more + - Developers can push to wiki repo. Protected branches does not affect wiki repo any more - Faster rev list - Fix branch removal @@ -143,7 +143,7 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" gem "gemnasium-gitlab-service", "~> 0.2" # Slack integration -gem "slack-notifier", "~> 0.3.2" +gem "slack-notifier", "~> 1.0.0" # d3 gem "d3_rails", "~> 3.1.4" diff --git a/Gemfile.lock b/Gemfile.lock index 800f33590cb..c283d4384fc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -488,7 +488,7 @@ GEM rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) six (0.2.0) - slack-notifier (0.3.2) + slack-notifier (1.0.0) slim (2.0.2) temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) @@ -689,7 +689,7 @@ DEPENDENCIES simplecov sinatra six - slack-notifier (~> 0.3.2) + slack-notifier (~> 1.0.0) slim spinach-rails spring (= 1.1.3) diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss index be4c4d07f1c..469f4f296ae 100644 --- a/app/assets/stylesheets/behaviors.scss +++ b/app/assets/stylesheets/behaviors.scss @@ -1,12 +1,22 @@ // Details //-------- -.js-details-container .content { display: none; } -.js-details-container .content.hide { display: block; } -.js-details-container.open .content { display: block; } -.js-details-container.open .content.hide { display: none; } +.js-details-container { + .content { + display: none; + &.hide { display: block; } + } + &.open .content { + display: block; + &.hide { display: none; } + } +} // Toggle between two states. -.js-toggler-container .turn-on { display: block; } -.js-toggler-container .turn-off { display: none; } -.js-toggler-container.on .turn-on { display: none; } -.js-toggler-container.on .turn-off { display: block; } +.js-toggler-container { + .turn-on { display: block; } + .turn-off { display: none; } + &.on { + .turn-on { display: none; } + .turn-off { display: block; } + } +} diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 9c611a1c147..8e209498323 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -66,7 +66,7 @@ module TreeHelper def tree_breadcrumbs(tree, max_links = 2) if @path.present? part_path = "" - parts = @path.split("\/") + parts = @path.split('/') yield('..', nil) if parts.count > max_links diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 553087946d6..f49708fd6eb 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -131,9 +131,10 @@ module Issuable users.concat(mentions.reduce([], :|)).uniq end - def to_hook_data + def to_hook_data(user) { object_kind: self.class.name.underscore, + user: user.hook_attrs, object_attributes: hook_attrs } end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 837002ef3c8..963f5440b6f 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -30,24 +30,20 @@ class SlackService < Service def fields [ - { type: 'text', name: 'webhook', placeholder: '' } + { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' } ] end def execute(push_data) + return unless webhook.present? + message = SlackMessage.new(push_data.merge( project_url: project_url, project_name: project_name )) - credentials = webhook.match(/([\w-]*).slack.com.*services\/(.*)/) - - if credentials.present? - subdomain = credentials[1] - token = credentials[2].split("token=").last - notifier = Slack::Notifier.new(subdomain, token) - notifier.ping(message.pretext, attachments: message.attachments) - end + notifier = Slack::Notifier.new(webhook) + notifier.ping(message.pretext, attachments: message.attachments) end private diff --git a/app/models/user.rb b/app/models/user.rb index 154cc0f3e16..d400edc0df5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -226,6 +226,11 @@ class User < ActiveRecord::Base where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%") end + def by_login(login) + where('lower(username) = :value OR lower(email) = :value', + value: login.to_s.downcase).first + end + def by_username_or_id(name_or_id) where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first end @@ -493,6 +498,14 @@ class User < ActiveRecord::Base end end + def hook_attrs + { + name: name, + username: username, + avatar_url: avatar_url + } + end + def ensure_namespace_correct # Ensure user has namespace self.create_namespace!(path: self.username, name: self.username) unless self.namespace diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 8f2b0e347f6..3f5222c93f1 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -160,19 +160,19 @@ class GitPushService ref_parts = ref.split('/') # Return if this is not a push to a branch (e.g. new commits) - ref_parts[1] =~ /heads/ && oldrev != "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && oldrev != Gitlab::Git::BLANK_SHA end def push_to_new_branch?(ref, oldrev) ref_parts = ref.split('/') - ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && oldrev == Gitlab::Git::BLANK_SHA end def push_remove_branch?(ref, newrev) ref_parts = ref.split('/') - ref_parts[1] =~ /heads/ && newrev == "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && newrev == Gitlab::Git::BLANK_SHA end def push_to_branch?(ref) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 41948f226a6..755c0ef45a8 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -4,7 +4,7 @@ module Issues private def execute_hooks(issue, action = 'open') - issue_data = issue.to_hook_data + issue_data = issue.to_hook_data(current_user) issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id) issue_data[:object_attributes].merge!(url: issue_url, action: action) issue.project.execute_hooks(issue_data, :issue_hooks) diff --git a/app/services/merge_requests/base_merge_service.rb b/app/services/merge_requests/base_merge_service.rb index 9bc50d3d16c..700a21ca011 100644 --- a/app/services/merge_requests/base_merge_service.rb +++ b/app/services/merge_requests/base_merge_service.rb @@ -13,7 +13,8 @@ module MergeRequests def execute_project_hooks(merge_request) if merge_request.project - merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) + hook_data = merge_request.to_hook_data(current_user) + merge_request.project.execute_hooks(hook_data, :merge_request_hooks) end end end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 694994001b0..7f3421b8e4b 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -7,7 +7,8 @@ module MergeRequests def execute_hooks(merge_request) if merge_request.project - merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) + hook_data = merge_request.to_hook_data(current_user) + merge_request.project.execute_hooks(hook_data, :merge_request_hooks) end end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index a6b68749a71..44e494525b3 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -18,7 +18,7 @@ class SystemHooksService def build_event_data(model, event) data = { event_name: build_event_name(model, event), - created_at: model.created_at + created_at: model.created_at.xmlschema } case model diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index b122b6c8658..29a55b36ca5 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -26,6 +26,10 @@ class AttachmentUploader < CarrierWave::Uploader::Base Gitlab.config.gitlab.relative_url_root + "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}" end + def url + Gitlab.config.gitlab.relative_url_root + super unless super.nil? + end + def file_storage? self.class.storage == CarrierWave::Storage::File end diff --git a/app/views/notify/new_ssh_key_email.html.haml b/app/views/notify/new_ssh_key_email.html.haml index deb0822d8f2..63b0cbbd205 100644 --- a/app/views/notify/new_ssh_key_email.html.haml +++ b/app/views/notify/new_ssh_key_email.html.haml @@ -6,5 +6,5 @@ title: %code= @key.title %p - If this key was added in error, you can remove it here: + If this key was added in error, you can remove it under = link_to "SSH Keys", profile_keys_url diff --git a/app/views/notify/new_ssh_key_email.text.erb b/app/views/notify/new_ssh_key_email.text.erb index 5f0080c2b76..05b551c89a0 100644 --- a/app/views/notify/new_ssh_key_email.text.erb +++ b/app/views/notify/new_ssh_key_email.text.erb @@ -2,6 +2,6 @@ Hi <%= @user.name %>! A new public key was added to your account: -title.................. <%= @key.title %> +Title: <%= @key.title %> -If this key was added in error, you can remove it here: <%= profile_keys_url %> +If this key was added in error, you can remove it at <%= profile_keys_url %> diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 64c19a57803..812d88a8730 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -23,5 +23,6 @@ tree_join(@commit.sha, @path)), class: 'btn btn-small' - if allowed_tree_edit? - = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do + = button_tag class: 'remove-blob btn btn-small btn-remove', + 'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do Remove diff --git a/doc/api/projects.md b/doc/api/projects.md index dfe3502b6e4..0055e2e476f 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -186,6 +186,7 @@ Parameters: "target_id": 830, "target_type": "Issue", "author_id": 1, + "author_username": "john", "data": null, "target_title": "Public project search field" }, @@ -196,6 +197,7 @@ Parameters: "target_id": null, "target_type": null, "author_id": 1, + "author_username": "john", "data": { "before": "50d4420237a9de7be1304607147aec22e4a14af7", "after": "c5feabde2d8cd023215af4d2ceeb7a64839fc428", @@ -231,6 +233,7 @@ Parameters: "target_id": 840, "target_type": "Issue", "author_id": 1, + "author_username": "john", "data": null, "target_title": "Finish & merge Code search PR" } diff --git a/doc/api/users.md b/doc/api/users.md index 3fdd3a75e88..20e0d68977e 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -78,7 +78,8 @@ GET /users "is_admin": false, "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "can_create_group": true, - "can_create_project": true + "can_create_project": true, + "projects_limit": 100 } ] ``` @@ -140,7 +141,8 @@ Parameters: "color_scheme_id": 2, "is_admin": false, "can_create_group": true, - "can_create_project": true + "can_create_project": true, + "projects_limit": 100 } ``` @@ -240,7 +242,8 @@ GET /user "color_scheme_id": 2, "is_admin": false, "can_create_group": true, - "can_create_project": true + "can_create_project": true, + "projects_limit": 100 } ``` diff --git a/doc/install/installation.md b/doc/install/installation.md index b5f54ff9b28..459a21ae821 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -127,7 +127,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Login to PostgreSQL sudo -u postgres psql -d template1 - # Create a user for GitLab. + # Create a user for GitLab + # Do not type the 'template1=#', this is part of the prompt template1=# CREATE USER git CREATEDB; # Create the GitLab production database & grant all privileges on database @@ -138,6 +139,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Try connecting to the new database with the new user sudo -u git -H psql -d gitlabhq_production + + # Quit the database session + gitlabhq_production> \q ## 5. Redis @@ -195,7 +199,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Make sure GitLab can write to the log/ and tmp/ directories sudo chown -R git log/ sudo chown -R git tmp/ - sudo chmod -R u+rwX log/ + sudo chmod -R u+rwX,go-w log/ sudo chmod -R u+rwX tmp/ # Create directory for satellites diff --git a/doc/integration/slack.md b/doc/integration/slack.md index 95cb0c6fae2..f2e73f272ef 100644 --- a/doc/integration/slack.md +++ b/doc/integration/slack.md @@ -4,15 +4,23 @@ To enable Slack integration you must create an Incoming WebHooks integration on Slack; -1. Sign in to [Slack](https://slack.com) (https://YOURSUBDOMAIN.slack.com/services) -1. Click on the Integrations menu at the top of the page. -1. Add a new Integration. +1. [Sign in to Slack](https://slack.com/signin) + +1. Select **Configure Integrations** from the dropdown next to your team name. + +1. Select the **All Services** tab + +1. Click **Add** next to Incoming Webhooks + 1. Pick Incoming WebHooks -1. Choose the channel name you want to send notifications to, in the Settings section -1. Add Integrations. - - Optional step; You can change bot's name and avatar by clicking "change the name of your bot", and "change the icon" after that you have to click "Save settings". -Now, Slack is ready to get external hooks. Before you leave this page don't forget to get the Token that you'll need on GitLab. You can find it by clicking Expand button, located in the "Instructions for creating Incoming WebHooks" section. It's a random alpha-numeric text 24 characters long. +1. Choose the channel name you want to send notifications to + +1. Click **Add Incoming WebHooks Integration**Add Integrations. + - Optional step; You can change bot's name and avatar by clicking modifying the bot name or avatar under **Integration Settings**. + +1. Copy the **Webhook URL**, we'll need this later for GitLab. + ## On GitLab @@ -26,10 +34,8 @@ After Slack is ready we need to setup GitLab. Here are the steps to achieve this 1. Fill in your Slack details - - Mark as active it - - Type your subdomain's prefix (If your subdomain is https://somedomain.slack.com you only have to type the somedomain) - - Type in the token you got from Slack - - Type in the channel name you want to use (eg. #announcements) + - Mark it as active + - Paste in the webhook url you got from Slack Have fun :) diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 31791da8074..f19517c0f18 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -63,6 +63,11 @@ Triggered when a new issue is created or an existing issue was updated/closed/re ```json { "object_kind": "issue", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, "object_attributes": { "id": 301, "title": "New API: create/update/delete file", @@ -92,6 +97,11 @@ Triggered when a new merge request is created or an existing merge request was u ```json { "object_kind": "merge_request", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, "object_attributes": { "id": 99, "target_branch": "master", diff --git a/features/steps/dashboard/event_filters.rb b/features/steps/dashboard/event_filters.rb index 332bfa95d97..3da3d62d0c0 100644 --- a/features/steps/dashboard/event_filters.rb +++ b/features/steps/dashboard/event_filters.rb @@ -29,7 +29,7 @@ class Spinach::Features::EventFilters < Spinach::FeatureSteps step 'this project has push event' do data = { - before: "0000000000000000000000000000000000000000", + before: Gitlab::Git::BLANK_SHA, after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", ref: "refs/heads/new_design", user_id: @user.id, diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 17d62210d10..d5d58070d86 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -109,12 +109,12 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps step 'I fill Slack settings' do check 'Active' - fill_in 'Webhook', with: 'https://gitlabhq.slack.com/services/hooks?token=cdIj4r4LfXUOySDUjp0tk3OI' + fill_in 'Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' click_button 'Save' end step 'I should see Slack service settings saved' do - find_field('Webhook').value.should == 'https://gitlabhq.slack.com/services/hooks?token=cdIj4r4LfXUOySDUjp0tk3OI' + find_field('Webhook').value.should == 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' end step 'I click Pushover service link' do diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 665f5d6d195..ddd501d4f88 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -78,7 +78,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I click on "Remove"' do - click_link 'Remove' + click_button 'Remove' end step 'I click on "Remove file"' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 4b833850a1c..bd7e6e1d8b3 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -32,7 +32,7 @@ module SharedProject @project = Project.find_by(name: "Shop") data = { - before: "0000000000000000000000000000000000000000", + before: Gitlab::Git::BLANK_SHA, after: "6d394385cf567f80a8fd85055db1ab4c5295806f", ref: "refs/heads/fix", user_id: @user.id, diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d19caf5b23a..40696489ba2 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -16,7 +16,8 @@ module API class UserFull < User expose :email - expose :theme_id, :color_scheme_id, :extern_uid, :provider + expose :theme_id, :color_scheme_id, :extern_uid, :provider, \ + :projects_limit expose :can_create_group?, as: :can_create_group expose :can_create_project?, as: :can_create_project end @@ -183,6 +184,12 @@ module API expose :target_id, :target_type, :author_id expose :data, :target_title expose :created_at + + expose :author_username do |event, options| + if event.author + event.author.username + end + end end class Namespace < Grape::Entity diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index ae33c529b93..30509528b8b 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -1,7 +1,7 @@ module Gitlab class Auth def find(login, password) - user = User.find_by(email: login) || User.find_by(username: login) + user = User.by_login(login) # If no user is found, or it's an LDAP server, try LDAP. # LDAP users are only authenticated via LDAP diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index ddb1ac61bf5..cc320da751c 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -16,7 +16,8 @@ module Gitlab # add_repository("gitlab/gitlab-ci") # def add_repository(name) - system gitlab_shell_projects_path, 'add-project', "#{name}.git" + Gitlab::Utils.system_silent([gitlab_shell_projects_path, + 'add-project', "#{name}.git"]) end # Import repository @@ -27,7 +28,8 @@ module Gitlab # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") # def import_repository(name, url) - system gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240' + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'import-project', + "#{name}.git", url, '240']) end # Move repository @@ -39,7 +41,8 @@ module Gitlab # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git") # def mv_repository(path, new_path) - system gitlab_shell_projects_path, 'mv-project', "#{path}.git", "#{new_path}.git" + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'mv-project', + "#{path}.git", "#{new_path}.git"]) end # Update HEAD for repository @@ -51,7 +54,8 @@ module Gitlab # update_repository_head("gitlab/gitlab-ci", "3-1-stable") # def update_repository_head(path, branch) - system gitlab_shell_projects_path, 'update-head', "#{path}.git", branch + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'update-head', + "#{path}.git", branch]) end # Fork repository to new namespace @@ -63,7 +67,8 @@ module Gitlab # fork_repository("gitlab/gitlab-ci", "randx") # def fork_repository(path, fork_namespace) - system gitlab_shell_projects_path, 'fork-project', "#{path}.git", fork_namespace + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'fork-project', + "#{path}.git", fork_namespace]) end # Remove repository from file system @@ -74,7 +79,8 @@ module Gitlab # remove_repository("gitlab/gitlab-ci") # def remove_repository(name) - system gitlab_shell_projects_path, 'rm-project', "#{name}.git" + Gitlab::Utils.system_silent([gitlab_shell_projects_path, + 'rm-project', "#{name}.git"]) end # Add repository branch from passed ref @@ -87,7 +93,8 @@ module Gitlab # add_branch("gitlab/gitlab-ci", "4-0-stable", "master") # def add_branch(path, branch_name, ref) - system gitlab_shell_projects_path, 'create-branch', "#{path}.git", branch_name, ref + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'create-branch', + "#{path}.git", branch_name, ref]) end # Remove repository branch @@ -99,7 +106,8 @@ module Gitlab # rm_branch("gitlab/gitlab-ci", "4-0-stable") # def rm_branch(path, branch_name) - system gitlab_shell_projects_path, 'rm-branch', "#{path}.git", branch_name + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'rm-branch', + "#{path}.git", branch_name]) end # Add repository tag from passed ref @@ -117,7 +125,7 @@ module Gitlab cmd = %W(#{gitlab_shell_path}/bin/gitlab-projects create-tag #{path}.git #{tag_name} #{ref}) cmd << message unless message.nil? || message.empty? - system *cmd + Gitlab::Utils.system_silent(cmd) end # Remove repository tag @@ -129,7 +137,8 @@ module Gitlab # rm_tag("gitlab/gitlab-ci", "v4.0") # def rm_tag(path, tag_name) - system gitlab_shell_projects_path, 'rm-tag', "#{path}.git", tag_name + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'rm-tag', + "#{path}.git", tag_name]) end # Add new key to gitlab-shell @@ -138,7 +147,8 @@ module Gitlab # add_key("key-42", "sha-rsa ...") # def add_key(key_id, key_content) - system gitlab_shell_keys_path, 'add-key', key_id, key_content + Gitlab::Utils.system_silent([gitlab_shell_keys_path, + 'add-key', key_id, key_content]) end # Batch-add keys to authorized_keys @@ -157,7 +167,8 @@ module Gitlab # remove_key("key-342", "sha-rsa ...") # def remove_key(key_id, key_content) - system gitlab_shell_keys_path, 'rm-key', key_id, key_content + Gitlab::Utils.system_silent([gitlab_shell_keys_path, + 'rm-key', key_id, key_content]) end # Remove all ssh keys from gitlab shell @@ -166,7 +177,7 @@ module Gitlab # remove_all_keys # def remove_all_keys - system gitlab_shell_keys_path, 'clear' + Gitlab::Utils.system_silent([gitlab_shell_keys_path, 'clear']) end # Add empty directory for storing repositories diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb new file mode 100644 index 00000000000..67aca5e36e9 --- /dev/null +++ b/lib/gitlab/git.rb @@ -0,0 +1,5 @@ +module Gitlab + module Git + BLANK_SHA = '0' * 40 + end +end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index b768a99a0e8..129881060d5 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -67,7 +67,7 @@ module Gitlab if forced_push?(project, oldrev, newrev) :force_push_code_to_protected_branches # and we dont allow remove of protected branch - elsif newrev =~ /0000000/ + elsif newrev == Gitlab::Git::BLANK_SHA :remove_protected_branches else :push_code_to_protected_branches @@ -85,7 +85,7 @@ module Gitlab def forced_push?(project, oldrev, newrev) return false if project.empty_repo? - if oldrev !~ /00000000/ && newrev !~ /00000000/ + if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read missed_refs.split("\n").size > 0 else diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb index 13cb08948bb..0fdd4dbe577 100644 --- a/lib/gitlab/git_ref_validator.rb +++ b/lib/gitlab/git_ref_validator.rb @@ -5,7 +5,8 @@ module Gitlab # # Returns true for a valid reference name, false otherwise def validate(ref_name) - system *%W(git check-ref-format refs/#{ref_name}) + Gitlab::Utils.system_silent( + %W(git check-ref-format refs/#{ref_name})) == 0 end end end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 4b8038843b0..c4d0d85b7f5 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -67,8 +67,7 @@ module Gitlab def default_regex_message "can contain only letters, digits, '_', '-' and '.'. " \ - "It must start with letter, digit or '_', optionally preceeded by '.'. " \ - "It must not end in '.git'." + "Cannot start with '-' or end in '.git'" \ end def default_regex diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb new file mode 100644 index 00000000000..bc30364550a --- /dev/null +++ b/lib/gitlab/utils.rb @@ -0,0 +1,14 @@ +module Gitlab + module Utils + extend self + + # Run system command without outputting to stdout. + # + # @param cmd [Array<String>] + # @return [Integer] exit status + def system_silent(cmd) + IO.popen(cmd).close + $?.exitstatus + end + end +end diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 1f3e1a4a3c1..95fc7e16a11 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -10,13 +10,21 @@ describe Gitlab::Auth do password: password, password_confirmation: password) end - let(:username) { 'john' } + let(:username) { 'John' } # username isn't lowercase, test this let(:password) { 'my-secret' } it "should find user by valid login/password" do expect( gl_auth.find(username, password) ).to eql user end + it 'should find user by valid email/password with case-insensitive email' do + expect(gl_auth.find(user.email.upcase, password)).to eql user + end + + it 'should find user by valid username/password with case-insensitive username' do + expect(gl_auth.find(username.upcase, password)).to eql user + end + it "should not find user with invalid password" do password = 'wrong' expect( gl_auth.find(username, password) ).to_not eql user diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 570b03827a8..fe0a6bbdabb 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -55,12 +55,13 @@ describe Gitlab::GitAccess do def changes { - push_new_branch: '000000000 570e7b2ab refs/heads/wow', + push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow", push_master: '6f6d7e7ed 570e7b2ab refs/heads/master', push_protected_branch: '6f6d7e7ed 570e7b2ab refs/heads/feature', - push_remove_protected_branch: '570e7b2ab 000000000 refs/heads/feature', + push_remove_protected_branch: "570e7b2ab #{Gitlab::Git::BLANK_SHA} "\ + 'refs/heads/feature', push_tag: '6f6d7e7ed 570e7b2ab refs/tags/v1.0.0', - push_new_tag: '000000000 570e7b2ab refs/tags/v7.8.9', + push_new_tag: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/tags/v7.8.9", push_all: ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'] } end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 10beafc4994..204ae9da704 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -36,7 +36,7 @@ describe Event do @user = project.owner data = { - before: "0000000000000000000000000000000000000000", + before: Gitlab::Git::BLANK_SHA, after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", ref: "refs/heads/master", user_id: @user.id, diff --git a/spec/models/slack_message_spec.rb b/spec/models/slack_message_spec.rb index 78d743e01bf..c530fad619b 100644 --- a/spec/models/slack_message_spec.rb +++ b/spec/models/slack_message_spec.rb @@ -26,11 +26,11 @@ describe SlackMessage do it 'returns a message regarding pushes' do subject.pretext.should == - 'user_name pushed to branch <url/commits/master|master> of ' << + 'user_name pushed to branch <url/commits/master|master> of '\ '<url|project_name> (<url/compare/before...after|Compare changes>)' subject.attachments.should == [ { - text: "<url1|abcdefghi>: message1 - author1\n" << + text: "<url1|abcdefghi>: message1 - author1\n"\ "<url2|123456789>: message2 - author2", color: color, } @@ -45,7 +45,7 @@ describe SlackMessage do it 'returns a message regarding a new branch' do subject.pretext.should == - 'user_name pushed new branch <url/commits/master|master> to ' << + 'user_name pushed new branch <url/commits/master|master> to '\ '<url|project_name>' subject.attachments.should be_empty end diff --git a/spec/models/slack_service_spec.rb b/spec/models/slack_service_spec.rb index 526165e397c..d4840391967 100644 --- a/spec/models/slack_service_spec.rb +++ b/spec/models/slack_service_spec.rb @@ -31,71 +31,27 @@ describe SlackService do end describe "Execute" do - let(:slack) { SlackService.new } - let(:slack_service) { SlackService.new } - let(:user) { create(:user) } + let(:slack) { SlackService.new } + let(:user) { create(:user) } let(:project) { create(:project) } let(:sample_data) { GitPushService.new.sample_data(project, user) } - let(:webhook) { 'https://gitlabhq.slack.com/services/hooks?token=cdIj4r4LfXUOySDUjp0tk3OI' } - let(:new_webhook) { 'https://hooks.gitlabhq.slack.com/services/cdIj4r4LfXUOySDUjp0tk3OI' } - let(:api_url) { - 'https://gitlabhq.slack.com/services/hooks/incoming-webhook?token=cdIj4r4LfXUOySDUjp0tk3OI' - } + let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } before do slack.stub( project: project, project_id: project.id, service_hook: true, - webhook: webhook + webhook: webhook_url ) - WebMock.stub_request(:post, api_url) + WebMock.stub_request(:post, webhook_url) end it "should call Slack API" do slack.execute(sample_data) - WebMock.should have_requested(:post, api_url).once - end - - context 'with new webhook syntax' do - before do - slack_service.stub( - project: project, - project_id: project.id, - service_hook: true, - webhook: new_webhook - ) - - WebMock.stub_request(:post, api_url) - end - - it "should call Slack API" do - slack_service.execute(sample_data) - - WebMock.should have_requested(:post, api_url).once - end - end - - context 'with new webhook syntax with slack allowed team name' do - before do - @allowed_webhook = 'https://gitlab-hq-123.slack.com/services/hooks/incoming-webhook?token=cdIj4r4LfXUOySDUjp0tk3OI' - slack_service.stub( - project: project, - project_id: project.id, - service_hook: true, - webhook: @allowed_webhook - ) - - WebMock.stub_request(:post, @allowed_webhook) - end - - it "should call Slack API" do - slack_service.execute(sample_data) - - WebMock.should have_requested(:post, @allowed_webhook).once - end + WebMock.should have_requested(:post, webhook_url).once end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6ad57b06e06..6d865cfc691 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -287,6 +287,20 @@ describe User do end end + describe '.by_login' do + let(:username) { 'John' } + let!(:user) { create(:user, username: username) } + + it 'should get the correct user' do + expect(User.by_login(user.email.upcase)).to eq user + expect(User.by_login(user.email)).to eq user + expect(User.by_login(username.downcase)).to eq user + expect(User.by_login(username)).to eq user + expect(User.by_login(nil)).to be_nil + expect(User.by_login('')).to be_nil + end + end + describe 'all_ssh_keys' do it { should have_many(:keys).dependent(:destroy) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ba7ec7b2be9..067935c82a6 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -209,9 +209,8 @@ describe API::API, api: true do json_response['message']['path'].should == [ 'can\'t be blank', 'is too short (minimum is 0 characters)', - 'can contain only letters, digits, \'_\', \'-\' and \'.\'. It must '\ - 'start with letter, digit or \'_\', optionally preceeded by \'.\'. '\ - 'It must not end in \'.git\'.' + 'can contain only letters, digits, \'_\', \'-\' and \'.\'. ' \ + 'Cannot start with \'-\' or end in \'.git\'' ] end @@ -339,6 +338,7 @@ describe API::API, api: true do json_event['action_name'].should == 'joined' json_event['project_id'].to_i.should == project.id + json_event['author_username'].should == user.username end it "should return a 404 error if not found" do diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index 013f425d6ce..57b2e6cbd6a 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -19,6 +19,32 @@ describe API::API, api: true do end end + context 'when email has case-typo and password is valid' do + it 'should return private token' do + post api('/session'), email: user.email.upcase, password: '12345678' + expect(response.status).to eq 201 + + expect(json_response['email']).to eq user.email + expect(json_response['private_token']).to eq user.private_token + expect(json_response['is_admin']).to eq user.is_admin? + expect(json_response['can_create_project']).to eq user.can_create_project? + expect(json_response['can_create_group']).to eq user.can_create_group? + end + end + + context 'when login has case-typo and password is valid' do + it 'should return private token' do + post api('/session'), login: user.username.upcase, password: '12345678' + expect(response.status).to eq 201 + + expect(json_response['email']).to eq user.email + expect(json_response['private_token']).to eq user.private_token + expect(json_response['is_admin']).to eq user.is_admin? + expect(json_response['can_create_project']).to eq user.can_create_project? + expect(json_response['can_create_group']).to eq user.can_create_group? + end + end + context "when invalid password" do it "should return authentication error" do post api("/session"), email: user.email, password: '123' diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index bc1598273be..a1a26d80a14 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -141,8 +141,7 @@ describe API::API, api: true do should == ['must be greater than or equal to 0'] json_response['message']['username']. should == ['can contain only letters, digits, '\ - '\'_\', \'-\' and \'.\'. It must start with letter, digit or '\ - '\'_\', optionally preceeded by \'.\'. It must not end in \'.git\'.'] + '\'_\', \'-\' and \'.\'. Cannot start with \'-\' or end in \'.git\''] end it "shouldn't available for non admin users" do @@ -285,8 +284,7 @@ describe API::API, api: true do should == ['must be greater than or equal to 0'] json_response['message']['username']. should == ['can contain only letters, digits, '\ - '\'_\', \'-\' and \'.\'. It must start with letter, digit or '\ - '\'_\', optionally preceeded by \'.\'. It must not end in \'.git\'.'] + '\'_\', \'-\' and \'.\'. Cannot start with \'-\' or end in \'.git\''] end context "with existing user" do @@ -433,6 +431,7 @@ describe API::API, api: true do json_response['is_admin'].should == user.is_admin? json_response['can_create_project'].should == user.can_create_project? json_response['can_create_group'].should == user.can_create_group? + json_response['projects_limit'].should == user.projects_limit end it "should return 401 error if user is unauthenticated" do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 4ef053a767f..19b442573f4 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -8,7 +8,7 @@ describe GitPushService do let (:service) { GitPushService.new } before do - @blankrev = '0000000000000000000000000000000000000000' + @blankrev = Gitlab::Git::BLANK_SHA @oldrev = sample_commit.parent_id @newrev = sample_commit.id @ref = 'refs/heads/master' diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 5f55871dc4a..e6db410fb1c 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -3,6 +3,16 @@ require 'rspec/mocks' module TestEnv extend self + # When developing the seed repository, comment out the branch you will modify. + BRANCH_SHA = { + 'feature' => '0b4bc9a', + 'feature_conflict' => 'bb5206f', + 'fix' => '12d65c8', + 'improve/awesome' => '5937ac0', + 'markdown' => '0ed8c6c', + 'master' => '5937ac0' + } + # Test environment # # See gitlab.yml.example test section for paths @@ -18,13 +28,13 @@ module TestEnv if File.directory?(tmp_test_path) Dir.entries(tmp_test_path).each do |entry| - unless ['.', '..', 'gitlab-shell'].include?(entry) + unless ['.', '..', 'gitlab-shell', factory_repo_name].include?(entry) FileUtils.rm_r(File.join(tmp_test_path, entry)) end end end - FileUtils.mkdir_p(tmp_test_path) + FileUtils.mkdir_p(repos_path) # Setup GitLab shell for test instance setup_gitlab_shell @@ -49,13 +59,32 @@ module TestEnv clone_url = "https://gitlab.com/gitlab-org/#{factory_repo_name}.git" unless File.directory?(factory_repo_path) - git_cmd = %W(git clone --bare #{clone_url} #{factory_repo_path}) - system(*git_cmd) + system(*%W(git clone #{clone_url} #{factory_repo_path})) + end + + Dir.chdir(factory_repo_path) do + BRANCH_SHA.each do |branch, sha| + # Try to reset without fetching to avoid using the network. + reset = %W(git update-ref refs/heads/#{branch} #{sha}) + unless system(*reset) + if system(*%w(git fetch origin)) + unless system(*reset) + raise 'The fetched test seed '\ + 'does not contain the required revision.' + end + else + raise 'Could not fetch test seed repository.' + end + end + end end + + # We must copy bare repositories because we will push to them. + system(*%W(git clone --bare #{factory_repo_path} #{factory_repo_path_bare})) end def copy_repo(project) - base_repo_path = File.expand_path(factory_repo_path) + base_repo_path = File.expand_path(factory_repo_path_bare) target_repo_path = File.expand_path(repos_path + "/#{project.namespace.path}/#{project.path}.git") FileUtils.mkdir_p(target_repo_path) FileUtils.cp_r("#{base_repo_path}/.", target_repo_path) @@ -69,7 +98,11 @@ module TestEnv private def factory_repo_path - @factory_repo_path ||= repos_path + "/root/#{factory_repo_name}.git" + @factory_repo_path ||= Rails.root.join('tmp', 'tests', factory_repo_name) + end + + def factory_repo_path_bare + factory_repo_path.to_s + '_bare' end def factory_repo_name |