From 8ed7391cb03ebf47efeb5ce5379eb667a3f22676 Mon Sep 17 00:00:00 2001 From: Vinnie Okada Date: Fri, 22 Aug 2014 10:40:25 -0500 Subject: Handle undefined text area values Check to see if a text area's `val` is defined before trying to call `replace()` on it. --- app/assets/javascripts/application.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 1960479321c..8ce3988383e 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -54,7 +54,7 @@ window.extractLast = (term) -> return split( term ).pop() window.rstrip = (val) -> - return val.replace(/\s+$/, '') + return if val then val.replace(/\s+$/, '') else val # Disable button if text field is empty window.disableButtonIfEmptyField = (field_selector, button_selector) -> -- cgit v1.2.1 From 6294033024d311efa14a9400eb0b8815c702fc44 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Fri, 26 Sep 2014 16:57:36 +0200 Subject: Use button type=submit instead of input. --- app/views/admin/groups/index.html.haml | 2 +- app/views/admin/groups/show.html.haml | 2 +- app/views/admin/projects/index.html.haml | 2 +- app/views/devise/sessions/_new_ldap.html.haml | 2 +- app/views/explore/groups/index.html.haml | 2 +- app/views/explore/projects/index.html.haml | 2 +- app/views/groups/members.html.haml | 2 +- app/views/layouts/_search.html.haml | 2 +- app/views/projects/blob/_remove.html.haml | 2 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/compare/_form.html.haml | 2 +- app/views/projects/tags/new.html.haml | 2 +- app/views/projects/team_members/import.html.haml | 2 +- app/views/search/show.html.haml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 9a0d5967927..e83c667767f 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -10,7 +10,7 @@ = form_tag admin_groups_path, method: :get, class: 'form-inline' do .form-group = text_field_tag :name, params[:name], class: "form-control input-mn-300" - = submit_tag "Search", class: "btn submit btn-primary" + = button_tag "Search", class: "btn submit btn-primary" %hr diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index d59d2a23179..bb660e60bd2 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -64,7 +64,7 @@ %div.prepend-top-10 = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr - = submit_tag 'Add users into group', class: "btn btn-create" + = button_tag 'Add users into group', class: "btn btn-create" .panel.panel-default .panel-heading %h3.panel-title diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 5ca6090f8d3..2cd6b12be7f 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -35,7 +35,7 @@ = label %hr = hidden_field_tag :sort, params[:sort] - = submit_tag "Search", class: "btn submit btn-primary" + = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_projects_path, class: "btn btn-cancel" .col-md-9 diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 6c5a878e904..be3cafab935 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -2,4 +2,4 @@ = text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"} %br/ - = submit_tag "LDAP Sign in", class: "btn-save btn" + = button_tag "LDAP Sign in", class: "btn-save btn" diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 80ddd5c1bde..b45ba920d1d 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -4,7 +4,7 @@ .form-group = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search" .form-group - = submit_tag 'Search', class: "btn btn-primary wide" + = button_tag 'Search', class: "btn btn-primary wide" .pull-right .dropdown.inline diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index c8bf78385e8..f797c4e3830 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -4,7 +4,7 @@ .form-group = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" .form-group - = submit_tag 'Search', class: "btn btn-primary wide" + = button_tag 'Search', class: "btn btn-primary wide" .pull-right .dropdown.inline diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml index ebf407d4ef1..9931fa5c3a6 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -13,7 +13,7 @@ = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' } - = submit_tag 'Search', class: 'btn' + = button_tag 'Search', class: 'btn' - if current_user && current_user.can?(:manage_group, @group) .pull-right diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 5ab82122ad7..2460a6a014d 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -8,7 +8,7 @@ - if @snippet || @snippets = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref - = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' + = button_tag 'Go' if ENV['RAILS_ENV'] == 'test' .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } :javascript diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index 93ffd4463b1..bc43f96b15d 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -19,7 +19,7 @@ .form-group .col-sm-2 .col-sm-10 - = submit_tag 'Remove file', class: 'btn btn-remove btn-remove-file' + = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 3f202f7ea6b..d2e307816bd 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -15,7 +15,7 @@ .col-sm-10 = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' .form-actions - = submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3 + = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' :javascript diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index da6157cf1b6..cb0a3747f7d 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -12,7 +12,7 @@ %span.input-group-addon to = text_field_tag :to, params[:to], class: "form-control"   - = submit_tag "Compare", class: "btn btn-create commits-compare-btn" + = button_tag "Compare", class: "btn btn-create commits-compare-btn" - if compare_to_mr_button? = link_to compare_mr_path, class: 'prepend-left-10 btn' do %strong Make a merge request diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 45ee61caf68..1c80e76ec7e 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -21,7 +21,7 @@ = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' .light (Optional) Entering a message will create an annotated tag. .form-actions - = submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3 + = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' :javascript diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/team_members/import.html.haml index 510b579fe2f..d1f46c61b2e 100644 --- a/app/views/projects/team_members/import.html.haml +++ b/app/views/projects/team_members/import.html.haml @@ -9,6 +9,6 @@ .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions - = submit_tag 'Import project members', class: "btn btn-create" + = button_tag 'Import project members', class: "btn btn-create" = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index bae57917a4c..5b4816e4c40 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -6,7 +6,7 @@ .col-sm-6 = search_field_tag :search, params[:search], placeholder: "issue 143", class: "form-control search-text-input", id: "dashboard_search" .col-sm-4 - = submit_tag 'Search', class: "btn btn-create" + = button_tag 'Search', class: "btn btn-create" .form-group .col-sm-2 - unless params[:snippets].eql? 'true' -- cgit v1.2.1 From 84fbd2935197c545703541e24d453f6e723293bf Mon Sep 17 00:00:00 2001 From: Bernhard Kaindl Date: Sat, 4 Oct 2014 11:44:20 +0200 Subject: transfer_service_spec: cleanup, merge common code, check against nil - replace creation of group2 with the use of group without add_owner(user) - fold TransferService calls into new test function transfer_project - remove currently not used (and not working) gitlab_shell stub (will submit testcase simulating failure in gitlab_shell separately) - add checks against not be_nil (result.should be_false passes even if nil) --- spec/services/projects/transfer_service_spec.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 2508dfc4565..79d0526ff89 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -3,15 +3,12 @@ require 'spec_helper' describe Projects::TransferService do let(:user) { create(:user) } let(:group) { create(:group) } - let(:group2) { create(:group) } let(:project) { create(:project, namespace: user.namespace) } context 'namespace -> namespace' do before do group.add_owner(user) - @service = Projects::TransferService.new(project, user, namespace_id: group.id) - @service.gitlab_shell.stub(mv_repository: true) - @result = @service.execute + @result = transfer_project(project, user, namespace_id: group.id) end it { @result.should be_true } @@ -20,24 +17,25 @@ describe Projects::TransferService do context 'namespace -> no namespace' do before do - group.add_owner(user) - @service = Projects::TransferService.new(project, user, namespace_id: nil) - @service.gitlab_shell.stub(mv_repository: true) - @result = @service.execute + @result = transfer_project(project, user, namespace_id: nil) end + it { @result.should_not be_nil } # { result.should be_false } passes on nil it { @result.should be_false } it { project.namespace.should == user.namespace } end context 'namespace -> not allowed namespace' do before do - @service = Projects::TransferService.new(project, user, namespace_id: group2.id) - @service.gitlab_shell.stub(mv_repository: true) - @result = @service.execute + @result = transfer_project(project, user, namespace_id: group.id) end + it { @result.should_not be_nil } # { result.should be_false } passes on nil it { @result.should be_false } it { project.namespace.should == user.namespace } end + + def transfer_project(project, user, params) + Projects::TransferService.new(project, user, params).execute + end end -- cgit v1.2.1 From 2aa2d268fd83e318263a2b93aae4f0db3210ef38 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Mon, 6 Oct 2014 09:30:25 -0700 Subject: support latest firefox esr release --- doc/install/requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 49edf36f574..8acd12ddfe0 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -90,7 +90,7 @@ On a very active server (10.000 active users) the Sidekiq process can use 1GB+ o ## Supported webbrowsers - Chrome (Latest stable version) -- Firefox (Latest released version) +- Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) - Safari 7+ (known problem: required fields in html5 do not work) - Opera (Latest released version) - IE 10+ -- cgit v1.2.1 From 68b5ac7f185b9b30cd862eacf806db726d8ce6e4 Mon Sep 17 00:00:00 2001 From: Vinnie Okada Date: Tue, 7 Oct 2014 14:55:15 -0500 Subject: Add option to keep repo on project delete Update the project API controller to use `Projects::DestroyService` instead of calling `Project#destroy` directly. Also add an optional parameter, `:keep_repo`, that allows a project to be deleted without deleting the repository, wiki, and satellite from disk. --- app/controllers/projects_controller.rb | 3 ++- app/services/projects/destroy_service.rb | 13 ++++++++----- lib/api/projects.rb | 8 +++++++- spec/features/projects_spec.rb | 7 ++++++- spec/requests/api/projects_spec.rb | 15 +++++++++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b3380a6ff23..c881c921ce9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -100,7 +100,8 @@ class ProjectsController < ApplicationController def destroy return access_denied! unless can?(current_user, :remove_project, project) - ::Projects::DestroyService.new(@project, current_user, {}).execute + ::Projects::DestroyService.new(@project, current_user, + keep_repo: params[:keep_repo]).execute respond_to do |format| format.html do diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 7e1d753b021..7c7892a0b14 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -6,7 +6,10 @@ module Projects project.team.truncate project.repository.expire_cache unless project.empty_repo? - if project.destroy + result = project.destroy + return false unless result + + unless params[:keep_repo] GitlabShellWorker.perform_async( :remove_repository, project.path_with_namespace @@ -18,11 +21,11 @@ module Projects ) project.satellite.destroy - - log_info("Project \"#{project.name}\" was removed") - system_hook_service.execute_hooks_for(project, :destroy) - true end + + log_info("Project \"#{project.name}\" was removed") + system_hook_service.execute_hooks_for(project, :destroy) + result end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 7f7d2f8e9a8..e70548d1e85 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -174,11 +174,17 @@ module API # # Parameters: # id (required) - The ID of a project + # keep_repo (optional) - If true, then delete the project from the + # database but keep the repo, wiki, and satellite on disk. # Example Request: # DELETE /projects/:id delete ":id" do authorize! :remove_project, user_project - user_project.destroy + ::Projects::DestroyService.new( + user_project, + current_user, + keep_repo: params[:keep_repo] + ).execute end # Mark this project as forked from another diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 524c4d5fa21..369db56d493 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -10,7 +10,12 @@ describe "Projects", feature: true do visit edit_project_path(@project) end - it "should be correct path" do + it 'should delete the project from the database and disk' do + expect(GitlabShellWorker).to( + receive(:perform_async).with(:remove_repository, + /#{@project.path_with_namespace}/) + ).twice + expect { click_link "Remove project" }.to change {Project.count}.by(-1) end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index aa1437c71aa..6de37cff0aa 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -632,10 +632,25 @@ describe API::API, api: true do describe "DELETE /projects/:id" do context "when authenticated as user" do it "should remove project" do + expect(GitlabShellWorker).to( + receive(:perform_async).with(:remove_repository, + /#{project.path_with_namespace}/) + ).twice + delete api("/projects/#{project.id}", user) response.status.should == 200 end + it 'should keep repo when "keep_repo" param is true' do + expect(GitlabShellWorker).not_to( + receive(:perform_async).with(:remove_repository, + /#{project.path_with_namespace}/) + ) + + delete api("/projects/#{project.id}?keep_repo=true", user) + response.status.should == 200 + end + it "should not remove a project if not an owner" do user3 = create(:user) project.team << [user3, :developer] -- cgit v1.2.1 From 3d705131f7d1a85dbf09053a200fbf36c6225625 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 13 Oct 2014 22:05:21 -0500 Subject: Added a password strength indicator to the profile page and the sign_up page, added CSS to best display it and created the custom script to load the meter. --- app/assets/javascripts/application.js.coffee | 2 + app/assets/javascripts/password_strength.js.coffee | 33 ++ app/assets/stylesheets/sections/profile.scss | 9 + app/views/devise/registrations/new.html.haml | 6 +- app/views/profiles/passwords/edit.html.haml | 2 +- .../javascripts/pwstrength-bootstrap-1.2.2.js | 659 +++++++++++++++++++++ 6 files changed, 707 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/password_strength.js.coffee create mode 100644 vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index ff0d0bb32b9..493babad856 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -18,6 +18,8 @@ #= require jquery.turbolinks #= require turbolinks #= require bootstrap +#= require pwstrength-bootstrap-1.2.2 +#= require password_strength #= require select2 #= require raphael #= require g.raphael-min diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee new file mode 100644 index 00000000000..e6fec307c59 --- /dev/null +++ b/app/assets/javascripts/password_strength.js.coffee @@ -0,0 +1,33 @@ +overwritten_messages = + wordSimilarToUsername: "Your password should not contain your username" + +overwritten_rules = + wordSequences: false + +$(document).ready -> + profileOptions = {} + profileOptions.ui = + container: "#password-strength" + showVerdictsInsideProgressBar: true + showPopover: true + showErrors: true + errorMessages: overwritten_messages + profileOptions.rules = + activated: overwritten_rules + + signUpOptions = {} + signUpOptions.common = + usernameField: "#user_username" + signUpOptions.ui = + container: "#password-strength" + showPopover: true + showErrors: true + showVerdicts: false + showProgressBar: false + showStatus: true + errorMessages: overwritten_messages + signUpOptions.rules = + activated: overwritten_rules + + $("#user_password").pwstrength profileOptions + $("#user_password_sign_up").pwstrength signUpOptions diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 086875582f3..2c2af7f52c5 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -111,3 +111,12 @@ height: 50px; } } + +//CSS for password-strength indicator +#password-strength { + margin-bottom: 0; +} + +.progress { + margin-top: 10px; +} diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index d6a952f3dc5..806d206d7b9 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -2,7 +2,7 @@ .login-heading %h3 Sign up .login-body - = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| + = form_for(resource, as: resource_name, url: registration_path(resource_name), role: 'form') do |f| .devise-errors = devise_error_messages! %div @@ -11,8 +11,8 @@ = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true %div = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true - %div - = f.password_field :password, class: "form-control middle", placeholder: "Password", required: true + .form-group#password-strength + = f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true %div diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 2a7d317aa3e..4440dcf338d 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -21,7 +21,7 @@ %div = link_to "Forgot your password?", reset_profile_password_path, method: :put - .form-group + .form-group#password-strength = f.label :password, 'New password', class: 'control-label' .col-sm-10 = f.password_field :password, required: true, class: 'form-control' diff --git a/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js b/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js new file mode 100644 index 00000000000..ee374a07fab --- /dev/null +++ b/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js @@ -0,0 +1,659 @@ +/*! + * jQuery Password Strength plugin for Twitter Bootstrap + * + * Copyright (c) 2008-2013 Tane Piper + * Copyright (c) 2013 Alejandro Blanco + * Dual licensed under the MIT and GPL licenses. + */ + +(function (jQuery) { +// Source: src/rules.js + + var rulesEngine = {}; + + try { + if (!jQuery && module && module.exports) { + var jQuery = require("jquery"), + jsdom = require("jsdom").jsdom; + jQuery = jQuery(jsdom().parentWindow); + } + } catch (ignore) {} + + (function ($, rulesEngine) { + "use strict"; + var validation = {}; + + rulesEngine.forbiddenSequences = [ + "0123456789", "abcdefghijklmnopqrstuvwxyz", "qwertyuiop", "asdfghjkl", + "zxcvbnm", "!@#$%^&*()_+" + ]; + + validation.wordNotEmail = function (options, word, score) { + if (word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i)) { + return score; + } + return 0; + }; + + validation.wordLength = function (options, word, score) { + var wordlen = word.length, + lenScore = Math.pow(wordlen, options.rules.raisePower); + if (wordlen < options.common.minChar) { + lenScore = (lenScore + score); + } + return lenScore; + }; + + validation.wordSimilarToUsername = function (options, word, score) { + var username = $(options.common.usernameField).val(); + if (username && word.toLowerCase().match(username.toLowerCase())) { + return score; + } + return 0; + }; + + validation.wordTwoCharacterClasses = function (options, word, score) { + if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) || + (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) || + (word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) { + return score; + } + return 0; + }; + + validation.wordRepetitions = function (options, word, score) { + if (word.match(/(.)\1\1/)) { return score; } + return 0; + }; + + validation.wordSequences = function (options, word, score) { + var found = false, + j; + if (word.length > 2) { + $.each(rulesEngine.forbiddenSequences, function (idx, seq) { + var sequences = [seq, seq.split('').reverse().join('')]; + $.each(sequences, function (idx, sequence) { + for (j = 0; j < (word.length - 2); j += 1) { // iterate the word trough a sliding window of size 3: + if (sequence.indexOf(word.toLowerCase().substring(j, j + 3)) > -1) { + found = true; + } + } + }); + }); + if (found) { return score; } + } + return 0; + }; + + validation.wordLowercase = function (options, word, score) { + return word.match(/[a-z]/) && score; + }; + + validation.wordUppercase = function (options, word, score) { + return word.match(/[A-Z]/) && score; + }; + + validation.wordOneNumber = function (options, word, score) { + return word.match(/\d+/) && score; + }; + + validation.wordThreeNumbers = function (options, word, score) { + return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score; + }; + + validation.wordOneSpecialChar = function (options, word, score) { + return word.match(/.[!,@,#,$,%,\^,&,*,?,_,~]/) && score; + }; + + validation.wordTwoSpecialChar = function (options, word, score) { + return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score; + }; + + validation.wordUpperLowerCombo = function (options, word, score) { + return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score; + }; + + validation.wordLetterNumberCombo = function (options, word, score) { + return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score; + }; + + validation.wordLetterNumberCharCombo = function (options, word, score) { + return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score; + }; + + rulesEngine.validation = validation; + + rulesEngine.executeRules = function (options, word) { + var totalScore = 0; + + $.each(options.rules.activated, function (rule, active) { + if (active) { + var score = options.rules.scores[rule], + funct = rulesEngine.validation[rule], + result, + errorMessage; + + if (!$.isFunction(funct)) { + funct = options.rules.extra[rule]; + } + + if ($.isFunction(funct)) { + result = funct(options, word, score); + if (result) { + totalScore += result; + } + if (result < 0 || (!$.isNumeric(result) && !result)) { + errorMessage = options.ui.spanError(options, rule); + if (errorMessage.length > 0) { + options.instances.errors.push(errorMessage); + } + } + } + } + }); + + return totalScore; + }; + }(jQuery, rulesEngine)); + + try { + if (module && module.exports) { + module.exports = rulesEngine; + } + } catch (ignore) {} + +// Source: src/options.js + + + + + var defaultOptions = {}; + + defaultOptions.common = {}; + defaultOptions.common.minChar = 6; + defaultOptions.common.usernameField = "#username"; + defaultOptions.common.userInputs = [ + // Selectors for input fields with user input + ]; + defaultOptions.common.onLoad = undefined; + defaultOptions.common.onKeyUp = undefined; + defaultOptions.common.zxcvbn = false; + defaultOptions.common.debug = false; + + defaultOptions.rules = {}; + defaultOptions.rules.extra = {}; + defaultOptions.rules.scores = { + wordNotEmail: -100, + wordLength: -50, + wordSimilarToUsername: -100, + wordSequences: -50, + wordTwoCharacterClasses: 2, + wordRepetitions: -25, + wordLowercase: 1, + wordUppercase: 3, + wordOneNumber: 3, + wordThreeNumbers: 5, + wordOneSpecialChar: 3, + wordTwoSpecialChar: 5, + wordUpperLowerCombo: 2, + wordLetterNumberCombo: 2, + wordLetterNumberCharCombo: 2 + }; + defaultOptions.rules.activated = { + wordNotEmail: true, + wordLength: true, + wordSimilarToUsername: true, + wordSequences: true, + wordTwoCharacterClasses: false, + wordRepetitions: false, + wordLowercase: true, + wordUppercase: true, + wordOneNumber: true, + wordThreeNumbers: true, + wordOneSpecialChar: true, + wordTwoSpecialChar: true, + wordUpperLowerCombo: true, + wordLetterNumberCombo: true, + wordLetterNumberCharCombo: true + }; + defaultOptions.rules.raisePower = 1.4; + + defaultOptions.ui = {}; + defaultOptions.ui.bootstrap2 = false; + defaultOptions.ui.showProgressBar = true; + defaultOptions.ui.showPopover = false; + defaultOptions.ui.showStatus = false; + defaultOptions.ui.spanError = function (options, key) { + "use strict"; + var text = options.ui.errorMessages[key]; + if (!text) { return ''; } + return '' + text + ''; + }; + defaultOptions.ui.errorMessages = { + wordLength: "Your password is too short", + wordNotEmail: "Do not use your email as your password", + wordSimilarToUsername: "Your password cannot contain your username", + wordTwoCharacterClasses: "Use different character classes", + wordRepetitions: "Too many repetitions", + wordSequences: "Your password contains sequences" + }; + defaultOptions.ui.verdicts = ["Weak", "Normal", "Medium", "Strong", "Very Strong"]; + defaultOptions.ui.showVerdicts = true; + defaultOptions.ui.showVerdictsInsideProgressBar = false; + defaultOptions.ui.showErrors = false; + defaultOptions.ui.container = undefined; + defaultOptions.ui.viewports = { + progress: undefined, + verdict: undefined, + errors: undefined + }; + defaultOptions.ui.scores = [14, 26, 38, 50]; + +// Source: src/ui.js + + + + + var ui = {}; + + (function ($, ui) { + "use strict"; + + var barClasses = ["danger", "warning", "success"], + statusClasses = ["error", "warning", "success"]; + + ui.getContainer = function (options, $el) { + var $container; + + $container = $(options.ui.container); + if (!($container && $container.length === 1)) { + $container = $el.parent(); + } + return $container; + }; + + ui.findElement = function ($container, viewport, cssSelector) { + if (viewport) { + return $container.find(viewport).find(cssSelector); + } + return $container.find(cssSelector); + }; + + ui.getUIElements = function (options, $el) { + var $container, result; + + if (options.instances.viewports) { + return options.instances.viewports; + } + + $container = ui.getContainer(options, $el); + + result = {}; + result.$progressbar = ui.findElement($container, options.ui.viewports.progress, "div.progress"); + if (options.ui.showVerdictsInsideProgressBar) { + result.$verdict = result.$progressbar.find("span.password-verdict"); + } + + if (!options.ui.showPopover) { + if (!options.ui.showVerdictsInsideProgressBar) { + result.$verdict = ui.findElement($container, options.ui.viewports.verdict, "span.password-verdict"); + } + result.$errors = ui.findElement($container, options.ui.viewports.errors, "ul.error-list"); + } + + options.instances.viewports = result; + return result; + }; + + ui.initProgressBar = function (options, $el) { + var $container = ui.getContainer(options, $el), + progressbar = "
"; + if (options.ui.showVerdictsInsideProgressBar) { + progressbar += ""; + } + progressbar += "
"; + + if (options.ui.viewports.progress) { + $container.find(options.ui.viewports.progress).append(progressbar); + } else { + $(progressbar).insertAfter($el); + } + }; + + ui.initHelper = function (options, $el, html, viewport) { + var $container = ui.getContainer(options, $el); + if (viewport) { + $container.find(viewport).append(html); + } else { + $(html).insertAfter($el); + } + }; + + ui.initVerdict = function (options, $el) { + ui.initHelper(options, $el, "", + options.ui.viewports.verdict); + }; + + ui.initErrorList = function (options, $el) { + ui.initHelper(options, $el, "
    ", + options.ui.viewports.errors); + }; + + ui.initPopover = function (options, $el) { + $el.popover("destroy"); + $el.popover({ + html: true, + placement: "top", + trigger: "manual", + content: " " + }); + }; + + ui.initUI = function (options, $el) { + if (options.ui.showPopover) { + ui.initPopover(options, $el); + } else { + if (options.ui.showErrors) { ui.initErrorList(options, $el); } + if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) { + ui.initVerdict(options, $el); + } + } + if (options.ui.showProgressBar) { + ui.initProgressBar(options, $el); + } + }; + + ui.possibleProgressBarClasses = ["danger", "warning", "success"]; + + ui.updateProgressBar = function (options, $el, cssClass, percentage) { + var $progressbar = ui.getUIElements(options, $el).$progressbar, + $bar = $progressbar.find(".progress-bar"), + cssPrefix = "progress-"; + + if (options.ui.bootstrap2) { + $bar = $progressbar.find(".bar"); + cssPrefix = ""; + } + + $.each(ui.possibleProgressBarClasses, function (idx, value) { + $bar.removeClass(cssPrefix + "bar-" + value); + }); + $bar.addClass(cssPrefix + "bar-" + barClasses[cssClass]); + $bar.css("width", percentage + '%'); + }; + + ui.updateVerdict = function (options, $el, text) { + var $verdict = ui.getUIElements(options, $el).$verdict; + $verdict.text(text); + }; + + ui.updateErrors = function (options, $el) { + var $errors = ui.getUIElements(options, $el).$errors, + html = ""; + $.each(options.instances.errors, function (idx, err) { + html += "
  • " + err + "
  • "; + }); + $errors.html(html); + }; + + ui.updatePopover = function (options, $el, verdictText) { + var popover = $el.data("bs.popover"), + html = "", + hide = true; + + if (options.ui.showVerdicts && + !options.ui.showVerdictsInsideProgressBar && + verdictText.length > 0) { + html = "
    " + verdictText + + "
    "; + hide = false; + } + if (options.ui.showErrors) { + html += "
      "; + $.each(options.instances.errors, function (idx, err) { + html += "
    • " + err + "
    • "; + hide = false; + }); + html += "
    "; + } + + if (hide) { + $el.popover("hide"); + return; + } + + if (options.ui.bootstrap2) { popover = $el.data("popover"); } + + if (popover.$arrow && popover.$arrow.parents("body").length > 0) { + $el.find("+ .popover .popover-content").html(html); + } else { + // It's hidden + popover.options.content = html; + $el.popover("show"); + } + }; + + ui.updateFieldStatus = function (options, $el, cssClass) { + var targetClass = options.ui.bootstrap2 ? ".control-group" : ".form-group", + $container = $el.parents(targetClass).first(); + + $.each(statusClasses, function (idx, css) { + if (!options.ui.bootstrap2) { css = "has-" + css; } + $container.removeClass(css); + }); + + cssClass = statusClasses[cssClass]; + if (!options.ui.bootstrap2) { cssClass = "has-" + cssClass; } + $container.addClass(cssClass); + }; + + ui.percentage = function (score, maximun) { + var result = Math.floor(100 * score / maximun); + result = result < 0 ? 0 : result; + result = result > 100 ? 100 : result; + return result; + }; + + ui.getVerdictAndCssClass = function (options, score) { + var cssClass, verdictText, level; + + if (score <= 0) { + cssClass = 0; + level = -1; + verdictText = options.ui.verdicts[0]; + } else if (score < options.ui.scores[0]) { + cssClass = 0; + level = 0; + verdictText = options.ui.verdicts[0]; + } else if (score < options.ui.scores[1]) { + cssClass = 0; + level = 1; + verdictText = options.ui.verdicts[1]; + } else if (score < options.ui.scores[2]) { + cssClass = 1; + level = 2; + verdictText = options.ui.verdicts[2]; + } else if (score < options.ui.scores[3]) { + cssClass = 1; + level = 3; + verdictText = options.ui.verdicts[3]; + } else { + cssClass = 2; + level = 4; + verdictText = options.ui.verdicts[4]; + } + + return [verdictText, cssClass, level]; + }; + + ui.updateUI = function (options, $el, score) { + var cssClass, barPercentage, verdictText; + + cssClass = ui.getVerdictAndCssClass(options, score); + verdictText = cssClass[0]; + cssClass = cssClass[1]; + + if (options.ui.showProgressBar) { + barPercentage = ui.percentage(score, options.ui.scores[3]); + ui.updateProgressBar(options, $el, cssClass, barPercentage); + if (options.ui.showVerdictsInsideProgressBar) { + ui.updateVerdict(options, $el, verdictText); + } + } + + if (options.ui.showStatus) { + ui.updateFieldStatus(options, $el, cssClass); + } + + if (options.ui.showPopover) { + ui.updatePopover(options, $el, verdictText); + } else { + if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) { + ui.updateVerdict(options, $el, verdictText); + } + if (options.ui.showErrors) { + ui.updateErrors(options, $el); + } + } + }; + }(jQuery, ui)); + +// Source: src/methods.js + + + + + var methods = {}; + + (function ($, methods) { + "use strict"; + var onKeyUp, applyToAll; + + onKeyUp = function (event) { + var $el = $(event.target), + options = $el.data("pwstrength-bootstrap"), + word = $el.val(), + userInputs, + verdictText, + verdictLevel, + score; + + if (options === undefined) { return; } + + options.instances.errors = []; + if (options.common.zxcvbn) { + userInputs = []; + $.each(options.common.userInputs, function (idx, selector) { + userInputs.push($(selector).val()); + }); + userInputs.push($(options.common.usernameField).val()); + score = zxcvbn(word, userInputs).entropy; + } else { + score = rulesEngine.executeRules(options, word); + } + ui.updateUI(options, $el, score); + verdictText = ui.getVerdictAndCssClass(options, score); + verdictLevel = verdictText[2]; + verdictText = verdictText[0]; + + if (options.common.debug) { console.log(score + ' - ' + verdictText); } + + if ($.isFunction(options.common.onKeyUp)) { + options.common.onKeyUp(event, { + score: score, + verdictText: verdictText, + verdictLevel: verdictLevel + }); + } + }; + + methods.init = function (settings) { + this.each(function (idx, el) { + // Make it deep extend (first param) so it extends too the + // rules and other inside objects + var clonedDefaults = $.extend(true, {}, defaultOptions), + localOptions = $.extend(true, clonedDefaults, settings), + $el = $(el); + + localOptions.instances = {}; + $el.data("pwstrength-bootstrap", localOptions); + $el.on("keyup", onKeyUp); + $el.on("change", onKeyUp); + $el.on("onpaste", onKeyUp); + + ui.initUI(localOptions, $el); + if ($.trim($el.val())) { // Not empty, calculate the strength + $el.trigger("keyup"); + } + + if ($.isFunction(localOptions.common.onLoad)) { + localOptions.common.onLoad(); + } + }); + + return this; + }; + + methods.destroy = function () { + this.each(function (idx, el) { + var $el = $(el), + options = $el.data("pwstrength-bootstrap"), + elements = ui.getUIElements(options, $el); + elements.$progressbar.remove(); + elements.$verdict.remove(); + elements.$errors.remove(); + $el.removeData("pwstrength-bootstrap"); + }); + }; + + methods.forceUpdate = function () { + this.each(function (idx, el) { + var event = { target: el }; + onKeyUp(event); + }); + }; + + methods.addRule = function (name, method, score, active) { + this.each(function (idx, el) { + var options = $(el).data("pwstrength-bootstrap"); + + options.rules.activated[name] = active; + options.rules.scores[name] = score; + options.rules.extra[name] = method; + }); + }; + + applyToAll = function (rule, prop, value) { + this.each(function (idx, el) { + $(el).data("pwstrength-bootstrap").rules[prop][rule] = value; + }); + }; + + methods.changeScore = function (rule, score) { + applyToAll.call(this, rule, "scores", score); + }; + + methods.ruleActive = function (rule, active) { + applyToAll.call(this, rule, "activated", active); + }; + + $.fn.pwstrength = function (method) { + var result; + + if (methods[method]) { + result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === "object" || !method) { + result = methods.init.apply(this, arguments); + } else { + $.error("Method " + method + " does not exist on jQuery.pwstrength-bootstrap"); + } + + return result; + }; + }(jQuery, methods)); +}(jQuery)); \ No newline at end of file -- cgit v1.2.1 From b66a1527356d808f418bab273f821c83a4365c90 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sun, 19 Oct 2014 10:50:23 +0200 Subject: Factor abilities methods in app controller, user model and services. --- app/controllers/application_controller.rb | 7 +------ app/controllers/explore/groups_controller.rb | 3 +-- app/controllers/explore/projects_controller.rb | 3 +-- app/models/ability.rb | 8 ++++++++ app/models/user.rb | 6 +----- app/services/base_service.rb | 6 +----- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 13d8d2a3e0a..f50889d62b9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,7 +5,6 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! before_filter :reject_blocked! before_filter :check_password_expiration - before_filter :add_abilities before_filter :ldap_security_check before_filter :dev_tools if Rails.env == 'development' before_filter :default_headers @@ -73,7 +72,7 @@ class ApplicationController < ActionController::Base end def abilities - @abilities ||= Six.new + Ability.abilities end def can?(object, action, subject) @@ -111,10 +110,6 @@ class ApplicationController < ActionController::Base nil end - def add_abilities - abilities << Ability - end - def authorize_project!(action) return access_denied! unless can?(current_user, action, project) end diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index f8e1a31e0b3..ada7031fea4 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,7 +1,6 @@ class Explore::GroupsController < ApplicationController skip_before_filter :authenticate_user!, - :reject_blocked, :set_current_user_for_observers, - :add_abilities + :reject_blocked, :set_current_user_for_observers layout "explore" diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index b6fa8b7e387..d75fd8e72fa 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,7 +1,6 @@ class Explore::ProjectsController < ApplicationController skip_before_filter :authenticate_user!, - :reject_blocked, - :add_abilities + :reject_blocked layout 'explore' diff --git a/app/models/ability.rb b/app/models/ability.rb index e155abc1449..97a72bf3635 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -262,5 +262,13 @@ class Ability end rules end + + def abilities + @abilities ||= begin + abilities = Six.new + abilities << self + abilities + end + end end end diff --git a/app/models/user.rb b/app/models/user.rb index 42faea0070e..154cc0f3e16 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -330,11 +330,7 @@ class User < ActiveRecord::Base end def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end + Ability.abilities end def can_select_namespace? diff --git a/app/services/base_service.rb b/app/services/base_service.rb index ed286c04095..0d46eeaa18f 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -6,11 +6,7 @@ class BaseService end def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end + Ability.abilities end def can?(object, action, subject) -- cgit v1.2.1 From 91c96b3714a8f5753d9851ee8e2a859b201f6905 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 22 Oct 2014 12:40:41 -0500 Subject: Added a password strength indicator to the reset password view and the change password view after first login. Updated JS to work with the updated views. --- app/assets/javascripts/password_strength.js.coffee | 11 ++++++----- app/views/devise/passwords/edit.html.haml | 4 ++-- app/views/profiles/passwords/new.html.haml | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee index e6fec307c59..61e25deac4c 100644 --- a/app/assets/javascripts/password_strength.js.coffee +++ b/app/assets/javascripts/password_strength.js.coffee @@ -15,10 +15,10 @@ $(document).ready -> profileOptions.rules = activated: overwritten_rules - signUpOptions = {} - signUpOptions.common = + deviseOptions = {} + deviseOptions.common = usernameField: "#user_username" - signUpOptions.ui = + deviseOptions.ui = container: "#password-strength" showPopover: true showErrors: true @@ -26,8 +26,9 @@ $(document).ready -> showProgressBar: false showStatus: true errorMessages: overwritten_messages - signUpOptions.rules = + deviseOptions.rules = activated: overwritten_rules $("#user_password").pwstrength profileOptions - $("#user_password_sign_up").pwstrength signUpOptions + $("#user_password_sign_up").pwstrength deviseOptions + $("#user_password_recover").pwstrength deviseOptions diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 1326cc0aac9..f6cbf9b82ba 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -6,8 +6,8 @@ .devise-errors = devise_error_messages! = f.hidden_field :reset_password_token - %div - = f.password_field :password, class: "form-control top", placeholder: "New password", required: true + .form-group#password-strength + = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true .clearfix.append-bottom-10 diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index aef7348fd20..b52514668e3 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -14,7 +14,7 @@ .form-group = f.label :current_password, class: 'control-label' .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' - .form-group + .form-group#password-strength = f.label :password, class: 'control-label' .col-sm-10= f.password_field :password, required: true, class: 'form-control' .form-group -- cgit v1.2.1 From 7039c9868a3209d89f8306c65ca5b74f8e2ea2c0 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 22 Oct 2014 20:39:02 -0500 Subject: Updated the IDs of the fields, so that it wouldn't mess with many tests Updated some tests to match new IDs --- app/assets/javascripts/password_strength.js.coffee | 2 +- app/views/profiles/passwords/edit.html.haml | 2 +- app/views/profiles/passwords/new.html.haml | 2 +- features/steps/profile/profile.rb | 6 +++--- spec/features/users_spec.rb | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee index 61e25deac4c..696a5ccf0b9 100644 --- a/app/assets/javascripts/password_strength.js.coffee +++ b/app/assets/javascripts/password_strength.js.coffee @@ -29,6 +29,6 @@ $(document).ready -> deviseOptions.rules = activated: overwritten_rules - $("#user_password").pwstrength profileOptions + $("#user_password_profile").pwstrength profileOptions $("#user_password_sign_up").pwstrength deviseOptions $("#user_password_recover").pwstrength deviseOptions diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 4440dcf338d..8e84d312194 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -24,7 +24,7 @@ .form-group#password-strength = f.label :password, 'New password', class: 'control-label' .col-sm-10 - = f.password_field :password, required: true, class: 'form-control' + = f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .form-group = f.label :password_confirmation, class: 'control-label' .col-sm-10 diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index b52514668e3..746b3a721e1 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -16,7 +16,7 @@ .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' .form-group#password-strength = f.label :password, class: 'control-label' - .col-sm-10= f.password_field :password, required: true, class: 'form-control' + .col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .form-group = f.label :password_confirmation, class: 'control-label' .col-sm-10 diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index adfaefb1644..0f7f33fe8ce 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -58,7 +58,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I try change my password w/o old one' do within '.update-password' do - fill_in "user_password", with: "22233344" + fill_in "user_password_profile", with: "22233344" fill_in "user_password_confirmation", with: "22233344" click_button "Save" end @@ -67,7 +67,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I change my password' do within '.update-password' do fill_in "user_current_password", with: "12345678" - fill_in "user_password", with: "22233344" + fill_in "user_password_profile", with: "22233344" fill_in "user_password_confirmation", with: "22233344" click_button "Save" end @@ -76,7 +76,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I unsuccessfully change my password' do within '.update-password' do fill_in "user_current_password", with: "12345678" - fill_in "user_password", with: "password" + fill_in "user_password_profile", with: "password" fill_in "user_password_confirmation", with: "confirmation" click_button "Save" end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 7b831c48611..a1206989d39 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -11,7 +11,7 @@ describe 'Users', feature: true do fill_in "user_name", with: "Name Surname" fill_in "user_username", with: "Great" fill_in "user_email", with: "name@mail.com" - fill_in "user_password", with: "password1234" + fill_in "user_password_sign_up", with: "password1234" fill_in "user_password_confirmation", with: "password1234" expect { click_button "Sign up" }.to change {User.count}.by(1) end -- cgit v1.2.1 From 41518a467dcef61deca24ad2f6205c6fd5706e1b Mon Sep 17 00:00:00 2001 From: Vinnie Okada Date: Wed, 22 Oct 2014 21:08:19 -0500 Subject: Remove :keep_repo option Always delete repositories from the filesystem when deleting a project. --- app/controllers/projects_controller.rb | 3 +-- app/services/projects/destroy_service.rb | 13 +++++-------- lib/api/projects.rb | 8 +------- spec/requests/api/projects_spec.rb | 10 ---------- 4 files changed, 7 insertions(+), 27 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c881c921ce9..b3380a6ff23 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -100,8 +100,7 @@ class ProjectsController < ApplicationController def destroy return access_denied! unless can?(current_user, :remove_project, project) - ::Projects::DestroyService.new(@project, current_user, - keep_repo: params[:keep_repo]).execute + ::Projects::DestroyService.new(@project, current_user, {}).execute respond_to do |format| format.html do diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 7c7892a0b14..7e1d753b021 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -6,10 +6,7 @@ module Projects project.team.truncate project.repository.expire_cache unless project.empty_repo? - result = project.destroy - return false unless result - - unless params[:keep_repo] + if project.destroy GitlabShellWorker.perform_async( :remove_repository, project.path_with_namespace @@ -21,11 +18,11 @@ module Projects ) project.satellite.destroy - end - log_info("Project \"#{project.name}\" was removed") - system_hook_service.execute_hooks_for(project, :destroy) - result + log_info("Project \"#{project.name}\" was removed") + system_hook_service.execute_hooks_for(project, :destroy) + true + end end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index e70548d1e85..7fcf97d1ad6 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -174,17 +174,11 @@ module API # # Parameters: # id (required) - The ID of a project - # keep_repo (optional) - If true, then delete the project from the - # database but keep the repo, wiki, and satellite on disk. # Example Request: # DELETE /projects/:id delete ":id" do authorize! :remove_project, user_project - ::Projects::DestroyService.new( - user_project, - current_user, - keep_repo: params[:keep_repo] - ).execute + ::Projects::DestroyService.new(user_project, current_user, {}).execute end # Mark this project as forked from another diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6de37cff0aa..ba7ec7b2be9 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -641,16 +641,6 @@ describe API::API, api: true do response.status.should == 200 end - it 'should keep repo when "keep_repo" param is true' do - expect(GitlabShellWorker).not_to( - receive(:perform_async).with(:remove_repository, - /#{project.path_with_namespace}/) - ) - - delete api("/projects/#{project.id}?keep_repo=true", user) - response.status.should == 200 - end - it "should not remove a project if not an owner" do user3 = create(:user) project.team << [user3, :developer] -- cgit v1.2.1 From add5d43b6ea0f4fbe85af4c07afe9e549b700205 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 23 Oct 2014 14:01:44 -0300 Subject: Close #717 Add documentation changes to: * Set the permissions of the unix socket properly * Create the directory for the socket * Make sure tmpfiles.d persists said directory The first change makes sure gitlabl can connect to the socket. The latter two avoid redis from failing to start on systemd-based systems, including recent versions of Debian. --- doc/install/installation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/install/installation.md b/doc/install/installation.md index 7a39f2eec9f..ac6535b0c86 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -150,6 +150,17 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Enable Redis socket for default Debian / Ubuntu path echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf + # Grant permission to the socket to all members of the redis group + echo 'unixsocketperm 770' | sudo tee -a /etc/redis/redis.conf + + # Create the directory which contains the socket + mkdir /var/run/redis + chown redis:redis /var/run/redis + chmod 755 /var/run/redis + # Persist the directory which contains the socket, if applicable + if [ -d /etc/tmpfiles.d ]; then + echo 'd /var/run/redis 0755 redis redis 10d -' | sudo tee -a /etc/tmpfiles.d/redis.conf + fi # Activate the changes to redis.conf sudo service redis-server restart -- cgit v1.2.1 From 7fbc4f5b3ac2abfa8ad94cc228114462b7b94601 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Thu, 23 Oct 2014 17:49:48 -0500 Subject: Corrected the layout of the strength indicator to be more consistent throughout the application. Fixed a test that was looking for an outdated HTML ID --- app/assets/javascripts/password_strength.js.coffee | 6 +++--- app/assets/stylesheets/sections/profile.scss | 12 ++++++++++-- app/views/devise/passwords/edit.html.haml | 2 +- app/views/profiles/passwords/new.html.haml | 2 +- features/steps/profile/profile.rb | 2 +- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee index 696a5ccf0b9..7f4a9180ae9 100644 --- a/app/assets/javascripts/password_strength.js.coffee +++ b/app/assets/javascripts/password_strength.js.coffee @@ -7,10 +7,11 @@ overwritten_rules = $(document).ready -> profileOptions = {} profileOptions.ui = - container: "#password-strength" - showVerdictsInsideProgressBar: true + showProgressBar: false + showVerdicts: false showPopover: true showErrors: true + showStatus: true errorMessages: overwritten_messages profileOptions.rules = activated: overwritten_rules @@ -19,7 +20,6 @@ $(document).ready -> deviseOptions.common = usernameField: "#user_username" deviseOptions.ui = - container: "#password-strength" showPopover: true showErrors: true showVerdicts: false diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 2c2af7f52c5..fce0a703a0a 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -117,6 +117,14 @@ margin-bottom: 0; } -.progress { - margin-top: 10px; +.has-success input { + background-color: #C3FF88 !important; +} + +.has-error input { + background-color: #FFA0A0 !important; +} + +.has-warning input { + background-color: #FFEC8B !important; } diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index f6cbf9b82ba..182ca5e774b 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -6,7 +6,7 @@ .devise-errors = devise_error_messages! = f.hidden_field :reset_password_token - .form-group#password-strength + .form-group = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index 746b3a721e1..42d2d0db29c 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -14,7 +14,7 @@ .form-group = f.label :current_password, class: 'control-label' .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' - .form-group#password-strength + .form-group = f.label :password, class: 'control-label' .col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .form-group diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 0f7f33fe8ce..7d3bea7878e 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -146,7 +146,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I submit new password' do fill_in :user_current_password, with: '12345678' - fill_in :user_password, with: '12345678' + fill_in :user_password_profile, with: '12345678' fill_in :user_password_confirmation, with: '12345678' click_button "Set new password" end -- cgit v1.2.1 From 000af8d5d7087cadbfd8ad677fb941e74a8ee3c7 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Fri, 24 Oct 2014 17:19:35 -0500 Subject: Moved require from application.js to password_strength.js Corrected div id for profile password/edit Added first spinach tests --- app/assets/javascripts/application.js.coffee | 1 - app/assets/javascripts/password_strength.js.coffee | 1 + app/views/profiles/passwords/edit.html.haml | 2 +- features/profile/profile.feature | 19 ++++++++++++ features/steps/profile/profile.rb | 34 ++++++++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 493babad856..faf725109f5 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -18,7 +18,6 @@ #= require jquery.turbolinks #= require turbolinks #= require bootstrap -#= require pwstrength-bootstrap-1.2.2 #= require password_strength #= require select2 #= require raphael diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee index 7f4a9180ae9..33b4d2e0f69 100644 --- a/app/assets/javascripts/password_strength.js.coffee +++ b/app/assets/javascripts/password_strength.js.coffee @@ -1,3 +1,4 @@ +#= require pwstrength-bootstrap-1.2.2 overwritten_messages = wordSimilarToUsername: "Your password should not contain your username" diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 8e84d312194..425200ff523 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -21,7 +21,7 @@ %div = link_to "Forgot your password?", reset_profile_password_path, method: :put - .form-group#password-strength + .form-group = f.label :password, 'New password', class: 'control-label' .col-sm-10 = f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' diff --git a/features/profile/profile.feature b/features/profile/profile.feature index d2125e013bc..d7fa370fe2a 100644 --- a/features/profile/profile.feature +++ b/features/profile/profile.feature @@ -83,3 +83,22 @@ Feature: Profile Given I visit profile design page When I change my code preview theme Then I should receive feedback that the changes were saved + + @javascript + Scenario: I see the password strength indicator + Given I visit profile password page + When I try to set a weak password + Then I should see the input field yellow + + @javascript + Scenario: I see the password strength indicator error + Given I visit profile password page + When I try to set a short password + Then I should see the input field red + And I should see the password error message + + @javascript + Scenario: I see the password strength indicator with success + Given I visit profile password page + When I try to set a strong password + Then I should see the input field green \ No newline at end of file diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 7d3bea7878e..6d747b65bae 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -64,6 +64,24 @@ class Spinach::Features::Profile < Spinach::FeatureSteps end end + step 'I try to set a weak password' do + within '.update-password' do + fill_in "user_password_profile", with: "22233344" + end + end + + step 'I try to set a short password' do + within '.update-password' do + fill_in "user_password_profile", with: "short" + end + end + + step 'I try to set a strong password' do + within '.update-password' do + fill_in "user_password_profile", with: "Itulvo9z8uud%$" + end + end + step 'I change my password' do within '.update-password' do fill_in "user_current_password", with: "12345678" @@ -86,6 +104,22 @@ class Spinach::Features::Profile < Spinach::FeatureSteps page.should have_content "You must provide a valid current password" end + step 'I should see the input field yellow' do + page.should have_css 'div.has-warning' + end + + step 'I should see the input field green' do + page.should have_css 'div.has-success' + end + + step 'I should see the input field red' do + page.should have_css 'div.has-error' + end + + step 'I should see the password error message' do + page.should have_content 'Your password is too short' + end + step "I should see a password error message" do page.should have_content "Password confirmation doesn't match" end -- cgit v1.2.1 From 37f33393b2e6aa18c416cb7a55d8de80dde0af58 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Fri, 24 Oct 2014 19:17:00 -0500 Subject: Changed colors to match GitLab's red & green and softened the yellow. --- app/assets/stylesheets/sections/profile.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index fce0a703a0a..b9f4e317e9c 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -118,13 +118,13 @@ } .has-success input { - background-color: #C3FF88 !important; + background-color: #D6F1D7 !important; } .has-error input { - background-color: #FFA0A0 !important; + background-color: #F3CECE !important; } .has-warning input { - background-color: #FFEC8B !important; + background-color: #FFE9A4 !important; } -- cgit v1.2.1 From d7476123852a7164b63e097b4aac73b04d1eca5c Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 09:20:43 +0100 Subject: Failing feature for dashboard issues when user has authored issues on projects he is not a member of. --- features/steps/dashboard/issues.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb index 6b5f88e5895..2a5850d091b 100644 --- a/features/steps/dashboard/issues.rb +++ b/features/steps/dashboard/issues.rb @@ -10,6 +10,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps step 'I should see issues authored by me' do should_see(authored_issue) + should_see(authored_issue_on_public_project) should_not_see(assigned_issue) should_not_see(other_issue) end @@ -22,6 +23,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps step 'I have authored issues' do authored_issue + authored_issue_on_public_project end step 'I have assigned issues' do @@ -64,6 +66,10 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps @other_issue ||= create :issue, project: project end + def authored_issue_on_public_project + @authored_issue_on_public_project ||= create :issue, author: current_user, project: public_project + end + def project @project ||= begin project =create :project @@ -71,4 +77,8 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps project end end + + def public_project + @public_project ||= create :project, :public + end end -- cgit v1.2.1 From 93532432dce129e9075ffa18bc99d77019f63eb2 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 09:30:39 +0100 Subject: Failing feature for dashboard merge requests when user has authored issues on forked project source. --- features/steps/dashboard/merge_requests.rb | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb index 95c378fa201..64ba04079e6 100644 --- a/features/steps/dashboard/merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb @@ -10,6 +10,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps step 'I should see merge requests authored by me' do should_see(authored_merge_request) + should_see(authored_merge_request_from_fork) should_not_see(assigned_merge_request) should_not_see(other_merge_request) end @@ -22,6 +23,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps step 'I have authored merge requests' do authored_merge_request + authored_merge_request_from_fork end step 'I have assigned merge requests' do @@ -57,11 +59,26 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps end def authored_merge_request - @authored_merge_request ||= create :merge_request, source_branch: 'simple_merge_request', author: current_user, target_project: project, source_project: project + @authored_merge_request ||= create :merge_request, + source_branch: 'simple_merge_request', + author: current_user, + target_project: project, + source_project: project end def other_merge_request - @other_merge_request ||= create :merge_request, source_branch: '2_3_notes_fix', target_project: project, source_project: project + @other_merge_request ||= create :merge_request, + source_branch: '2_3_notes_fix', + target_project: project, + source_project: project + end + + def authored_merge_request_from_fork + @authored_merge_request_from_fork ||= create :merge_request, + source_branch: 'basic_page', + author: current_user, + target_project: public_project, + source_project: forked_project end def project @@ -71,4 +88,12 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps project end end + + def public_project + @public_project ||= create :project, :public + end + + def forked_project + @forked_project ||= Projects::ForkService.new(public_project, current_user).execute + end end -- cgit v1.2.1 From 5e017a4566495b0024ab065e7ceab279ff77e030 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 09:53:35 +0100 Subject: Assigned merge request should show on dashboard mr overiew feature spec. --- features/steps/dashboard/merge_requests.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb index 64ba04079e6..75e53173d3f 100644 --- a/features/steps/dashboard/merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb @@ -4,7 +4,9 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps step 'I should see merge requests assigned to me' do should_see(assigned_merge_request) + should_see(assigned_merge_request_from_fork) should_not_see(authored_merge_request) + should_not_see(authored_merge_request_from_fork) should_not_see(other_merge_request) end @@ -12,6 +14,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps should_see(authored_merge_request) should_see(authored_merge_request_from_fork) should_not_see(assigned_merge_request) + should_not_see(assigned_merge_request_from_fork) should_not_see(other_merge_request) end @@ -28,6 +31,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps step 'I have assigned merge requests' do assigned_merge_request + assigned_merge_request_from_fork end step 'I have other merge requests' do @@ -55,7 +59,10 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps end def assigned_merge_request - @assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project, source_project: project + @assigned_merge_request ||= create :merge_request, + assignee: current_user, + target_project: project, + source_project: project end def authored_merge_request @@ -81,6 +88,14 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps source_project: forked_project end + def assigned_merge_request_from_fork + @assigned_merge_request_from_fork ||= create :merge_request, + source_branch: 'basic_page_fix', + assignee: current_user, + target_project: public_project, + source_project: forked_project + end + def project @project ||= begin project =create :project -- cgit v1.2.1 From d3bdd3ba67dda8b8392770a2b6e4a7473ec4d42d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 10:02:20 +0100 Subject: Do not filter out issues and merge requests related to user right away. --- app/finders/issuable_finder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 56c4f22120d..d0574240511 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -48,7 +48,7 @@ class IssuableFinder else [] end - elsif current_user && params[:authorized_only].presence + elsif current_user && params[:authorized_only].presence && !current_user_related? klass.of_projects(current_user.authorized_projects).references(:project) else klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project) @@ -142,4 +142,8 @@ class IssuableFinder def project Project.where(id: params[:project_id]).first if params[:project_id].present? end + + def current_user_related? + params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' + end end -- cgit v1.2.1 From d504ca8a0c696b31eaf383f97f47e08afac23084 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 13:02:12 +0100 Subject: Add settings to disable email sending from GitLab. --- config/gitlab.yml.example | 2 ++ config/initializers/1_settings.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 2ca6abac576..bb0ffae0b70 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -39,6 +39,8 @@ production: &base # time_zone: 'UTC' ## Email settings + # Uncomment and set to false if you need to disable email sending from GitLab (default: true) + # email_enabled: true # Email address used in the "From" field in mails sent by GitLab email_from: example@example.com diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 4670791ddb0..27bb83784ba 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -95,6 +95,7 @@ Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" +Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil? Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['user'] ||= 'git' -- cgit v1.2.1 From d78e80fa74777e886ca131614f3b4d3f06bf9fff Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 13:05:50 +0100 Subject: Add email interceptor to prevent mail sending if email sending is disabled. --- config/initializers/disable_email_interceptor.rb | 2 ++ lib/disable_email_interceptor.rb | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 config/initializers/disable_email_interceptor.rb create mode 100644 lib/disable_email_interceptor.rb diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb new file mode 100644 index 00000000000..c76a6b8b19f --- /dev/null +++ b/config/initializers/disable_email_interceptor.rb @@ -0,0 +1,2 @@ +# Interceptor in lib/disable_email_interceptor.rb +ActionMailer::Base.register_interceptor(DisableEmailInterceptor) unless Gitlab.config.gitlab.email_enabled diff --git a/lib/disable_email_interceptor.rb b/lib/disable_email_interceptor.rb new file mode 100644 index 00000000000..1b80be112a4 --- /dev/null +++ b/lib/disable_email_interceptor.rb @@ -0,0 +1,8 @@ +# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails +class DisableEmailInterceptor + + def self.delivering_email(message) + message.perform_deliveries = false + Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}" + end +end -- cgit v1.2.1 From 8101640c33d240fc5b29c3dec33c56b67325a89f Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 27 Oct 2014 08:30:57 -0500 Subject: Change update recommendation --- doc/update/6.x-or-7.x-to-7.3.md | 286 ++++++++++++++++++++++++++++++++++++++++ doc/update/6.x-or-7.x-to-7.4.md | 286 ---------------------------------------- 2 files changed, 286 insertions(+), 286 deletions(-) create mode 100644 doc/update/6.x-or-7.x-to-7.3.md delete mode 100644 doc/update/6.x-or-7.x-to-7.4.md diff --git a/doc/update/6.x-or-7.x-to-7.3.md b/doc/update/6.x-or-7.x-to-7.3.md new file mode 100644 index 00000000000..66853634d38 --- /dev/null +++ b/doc/update/6.x-or-7.x-to-7.3.md @@ -0,0 +1,286 @@ +# From 6.x or 7.x to 7.3 + +This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.3. + +## Global issue numbers + +As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their URL. If you use an old issue number URL and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects. + +## Editable labels + +In GitLab 7.2 we replace Issue and Merge Request tags with labels, making it +possible to edit the label text and color. The characters `?`, `&` and `,` are +no longer allowed however so those will be removed from your tags during the +database migrations for GitLab 7.2. + +## 0. Stop server + + sudo service gitlab stop + +## 1. Backup + +It's useful to make a backup just in case things go south: +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version) + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +## 2. Update Ruby + +If you are still using Ruby 1.9.3 or below, you will need to update Ruby. +You can check which version you are running with `ruby -v`. + +If you are you running Ruby 2.0.x, you do not need to upgrade ruby, but can consider doing so for performance reasons. + +If you are running Ruby 2.1.1 consider upgrading to 2.1.2, because of the high memory usage of Ruby 2.1.1. + +Install, update dependencies: + +```bash +sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl +``` + +Download and compile Ruby: + +```bash +mkdir /tmp/ruby && cd /tmp/ruby +curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | tar xz +cd ruby-2.1.2 +./configure --disable-install-rdoc +make +sudo make install +``` + +Install Bundler: + +```bash +sudo gem install bundler --no-ri --no-rdoc +``` + +## 3. Get latest code + +```bash +cd /home/git/gitlab +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +``` + +For GitLab Community Edition: + +```bash +sudo -u git -H git checkout 7-3-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +sudo -u git -H git checkout 7-3-stable-ee +``` + +## 4. Install additional packages + +```bash +# Add support for lograte for better log file handling +sudo apt-get install logrotate + +# Install pkg-config and cmake, which is needed for the latest versions of rugged +sudo apt-get install pkg-config cmake +``` + +## 5. Configure Redis to use sockets + + # Configure redis to use sockets + sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig + # Disable Redis listening on TCP by setting 'port' to 0 + sed 's/^port .*/port 0/' /etc/redis/redis.conf.orig | sudo tee /etc/redis/redis.conf + # Enable Redis socket for default Debian / Ubuntu path + echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf + # Be sure redis group can write to the socket, enable only if supported (>= redis 2.4.0). + sudo sed -i '/# unixsocketperm/ s/^# unixsocketperm.*/unixsocketperm 0775/' /etc/redis/redis.conf + # Activate the changes to redis.conf + sudo service redis-server restart + # Add git to the redis group + sudo usermod -aG redis git + + # Configure Redis connection settings + sudo -u git -H cp config/resque.yml.example config/resque.yml + # Change the Redis socket path if you are not using the default Debian / Ubuntu configuration + sudo -u git -H editor config/resque.yml + + # Configure gitlab-shell to use Redis sockets + sudo -u git -H sed -i 's|^ # socket.*| socket: /var/run/redis/redis.sock|' /home/git/gitlab-shell/config.yml + +## 6. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch +sudo -u git -H git checkout v2.0.1 +``` + +## 7. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without ... postgres') +sudo -u git -H bundle install --without development test postgres --deployment + +# PostgreSQL installations (note: the line below states '--without ... mysql') +sudo -u git -H bundle install --without development test mysql --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Enable internal issue IDs (introduced in GitLab 6.1) +sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +# Close access to gitlab-satellites for others +sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites + +# Update init.d script +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +## 8. Update config files + +TIP: to see what changed in `gitlab.yml.example` in this release use next command: + +``` +git diff 6-0-stable:config/gitlab.yml.example 7-3-stable:config/gitlab.yml.example +``` + +* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/config/gitlab.yml.example but with your settings. +* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/config/unicorn.rb.example but with your settings. +* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.0.1/config.yml.example but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab-ssl but with your settings. +* Copy rack attack middleware config + +```bash +sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb +``` + +* Set up logrotate + +```bash +sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab +``` + +## 9. Start application + + sudo service gitlab start + sudo service nginx restart + +## 10. Check application status + +Check if GitLab and its environment are configured correctly: + + cd /home/git/gitlab + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check with: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations upgrade complete! + +## 11. Update OmniAuth configuration + +When using Google omniauth login, changes of the Google account required. +Ensure that `Contacts API` and the `Google+ API` are enabled in the [Google Developers Console](https://console.developers.google.com/). +More details can be found at the [integration documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/google.md). + +## 12. Optional optimizations for GitLab setups with MySQL databases + +Only applies if running MySQL database created with GitLab 6.7 or earlier. If you are not experiencing any issues you may not need the following instructions however following them will bring your database in line with the latest recommended installation configuration and help avoid future issues. Be sure to follow these directions exactly. These directions should be safe for any MySQL instance but to be sure make a current MySQL database backup beforehand. + +``` +# Stop GitLab +sudo service gitlab stop + +# Secure your MySQL installation (added in GitLab 6.2) +sudo mysql_secure_installation + +# Login to MySQL +mysql -u root -p + +# do not type the 'mysql>', this is part of the prompt + +# Convert all tables to use the InnoDB storage engine (added in GitLab 6.8) +SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `ENGINE` <> 'InnoDB' AND `TABLE_TYPE` = 'BASE TABLE'; + +# If previous query returned results, copy & run all outputed SQL statements + +# Convert all tables to correct character set +SET foreign_key_checks = 0; +SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `TABLE_COLLATION` <> 'utf8_unicode_ci' AND `TABLE_TYPE` = 'BASE TABLE'; + +# If previous query returned results, copy & run all outputed SQL statements + +# turn foreign key checks back on +SET foreign_key_checks = 1; + +# Find MySQL users +mysql> SELECT user FROM mysql.user WHERE user LIKE '%git%'; + +# If git user exists and gitlab user does not exist +# you are done with the database cleanup tasks +mysql> \q + +# If both users exist skip to Delete gitlab user + +# Create new user for GitLab (changed in GitLab 6.4) +# change $password in the command below to a real password you pick +mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; + +# Grant the git user necessary permissions on the database +mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlabhq_production`.* TO 'git'@'localhost'; + +# Delete the old gitlab user +mysql> DELETE FROM mysql.user WHERE user='gitlab'; + +# Quit the database session +mysql> \q + +# Try connecting to the new database with the new user +sudo -u git -H mysql -u git -p -D gitlabhq_production + +# Type the password you replaced $password with earlier + +# You should now see a 'mysql>' prompt + +# Quit the database session +mysql> \q + +# Update database configuration details +# See config/database.yml.mysql for latest recommended configuration details +# Remove the reaping_frequency setting line if it exists (removed in GitLab 6.8) +# Set production -> pool: 10 (updated in GitLab 5.3) +# Set production -> username: git +# Set production -> password: the password your replaced $password with earlier +sudo -u git -H editor /home/git/gitlab/config/database.yml + +## Things went south? Revert to previous version (6.0) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 5.4 to 6.0](5.4-to-6.0.md), except for the database migration (the backup is already migrated to the previous version). + +### 2. Restore from the backup: + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +## Login issues after upgrade? + +If running in HTTPS mode, be sure to read [Can't Verify CSRF token authenticity](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide#cant-verify-csrf-token-authenticitycant-get-past-login-pageredirected-to-login-page) diff --git a/doc/update/6.x-or-7.x-to-7.4.md b/doc/update/6.x-or-7.x-to-7.4.md deleted file mode 100644 index 2fa6889af73..00000000000 --- a/doc/update/6.x-or-7.x-to-7.4.md +++ /dev/null @@ -1,286 +0,0 @@ -# From 6.x or 7.x to 7.4 - -This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.4. - -## Global issue numbers - -As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their URL. If you use an old issue number URL and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects. - -## Editable labels - -In GitLab 7.2 we replace Issue and Merge Request tags with labels, making it -possible to edit the label text and color. The characters `?`, `&` and `,` are -no longer allowed however so those will be removed from your tags during the -database migrations for GitLab 7.2. - -## 0. Stop server - - sudo service gitlab stop - -## 1. Backup - -It's useful to make a backup just in case things go south: -(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version) - -```bash -cd /home/git/gitlab -sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production -``` - -## 2. Update Ruby - -If you are still using Ruby 1.9.3 or below, you will need to update Ruby. -You can check which version you are running with `ruby -v`. - -If you are you running Ruby 2.0.x, you do not need to upgrade ruby, but can consider doing so for performance reasons. - -If you are running Ruby 2.1.1 consider upgrading to 2.1.2, because of the high memory usage of Ruby 2.1.1. - -Install, update dependencies: - -```bash -sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl -``` - -Download and compile Ruby: - -```bash -mkdir /tmp/ruby && cd /tmp/ruby -curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | tar xz -cd ruby-2.1.2 -./configure --disable-install-rdoc -make -sudo make install -``` - -Install Bundler: - -```bash -sudo gem install bundler --no-ri --no-rdoc -``` - -## 3. Get latest code - -```bash -cd /home/git/gitlab -sudo -u git -H git fetch --all -sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically -``` - -For GitLab Community Edition: - -```bash -sudo -u git -H git checkout 7-4-stable -``` - -OR - -For GitLab Enterprise Edition: - -```bash -sudo -u git -H git checkout 7-4-stable-ee -``` - -## 4. Install additional packages - -```bash -# Add support for lograte for better log file handling -sudo apt-get install logrotate - -# Install pkg-config and cmake, which is needed for the latest versions of rugged -sudo apt-get install pkg-config cmake -``` - -## 5. Configure Redis to use sockets - - # Configure redis to use sockets - sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig - # Disable Redis listening on TCP by setting 'port' to 0 - sed 's/^port .*/port 0/' /etc/redis/redis.conf.orig | sudo tee /etc/redis/redis.conf - # Enable Redis socket for default Debian / Ubuntu path - echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf - # Be sure redis group can write to the socket, enable only if supported (>= redis 2.4.0). - sudo sed -i '/# unixsocketperm/ s/^# unixsocketperm.*/unixsocketperm 0775/' /etc/redis/redis.conf - # Activate the changes to redis.conf - sudo service redis-server restart - # Add git to the redis group - sudo usermod -aG redis git - - # Configure Redis connection settings - sudo -u git -H cp config/resque.yml.example config/resque.yml - # Change the Redis socket path if you are not using the default Debian / Ubuntu configuration - sudo -u git -H editor config/resque.yml - - # Configure gitlab-shell to use Redis sockets - sudo -u git -H sed -i 's|^ # socket.*| socket: /var/run/redis/redis.sock|' /home/git/gitlab-shell/config.yml - -## 6. Update gitlab-shell - -```bash -cd /home/git/gitlab-shell -sudo -u git -H git fetch -sudo -u git -H git checkout v2.0.1 -``` - -## 7. Install libs, migrations, etc. - -```bash -cd /home/git/gitlab - -# MySQL installations (note: the line below states '--without ... postgres') -sudo -u git -H bundle install --without development test postgres --deployment - -# PostgreSQL installations (note: the line below states '--without ... mysql') -sudo -u git -H bundle install --without development test mysql --deployment - -# Run database migrations -sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production - -# Enable internal issue IDs (introduced in GitLab 6.1) -sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production - -# Clean up assets and cache -sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production - -# Close access to gitlab-satellites for others -sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites - -# Update init.d script -sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab -``` - -## 8. Update config files - -TIP: to see what changed in `gitlab.yml.example` in this release use next command: - -``` -git diff 6-0-stable:config/gitlab.yml.example 7-4-stable:config/gitlab.yml.example -``` - -* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/gitlab.yml.example but with your settings. -* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/unicorn.rb.example but with your settings. -* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.0.1/config.yml.example but with your settings. -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/lib/support/nginx/gitlab but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/lib/support/nginx/gitlab-ssl but with your settings. -* Copy rack attack middleware config - -```bash -sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb -``` - -* Set up logrotate - -```bash -sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab -``` - -## 9. Start application - - sudo service gitlab start - sudo service nginx restart - -## 10. Check application status - -Check if GitLab and its environment are configured correctly: - - cd /home/git/gitlab - sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production - -To make sure you didn't miss anything run a more thorough check with: - - sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production - -If all items are green, then congratulations upgrade complete! - -## 11. Update OmniAuth configuration - -When using Google omniauth login, changes of the Google account required. -Ensure that `Contacts API` and the `Google+ API` are enabled in the [Google Developers Console](https://console.developers.google.com/). -More details can be found at the [integration documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/google.md). - -## 12. Optional optimizations for GitLab setups with MySQL databases - -Only applies if running MySQL database created with GitLab 6.7 or earlier. If you are not experiencing any issues you may not need the following instructions however following them will bring your database in line with the latest recommended installation configuration and help avoid future issues. Be sure to follow these directions exactly. These directions should be safe for any MySQL instance but to be sure make a current MySQL database backup beforehand. - -``` -# Stop GitLab -sudo service gitlab stop - -# Secure your MySQL installation (added in GitLab 6.2) -sudo mysql_secure_installation - -# Login to MySQL -mysql -u root -p - -# do not type the 'mysql>', this is part of the prompt - -# Convert all tables to use the InnoDB storage engine (added in GitLab 6.8) -SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `ENGINE` <> 'InnoDB' AND `TABLE_TYPE` = 'BASE TABLE'; - -# If previous query returned results, copy & run all outputed SQL statements - -# Convert all tables to correct character set -SET foreign_key_checks = 0; -SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `TABLE_COLLATION` <> 'utf8_unicode_ci' AND `TABLE_TYPE` = 'BASE TABLE'; - -# If previous query returned results, copy & run all outputed SQL statements - -# turn foreign key checks back on -SET foreign_key_checks = 1; - -# Find MySQL users -mysql> SELECT user FROM mysql.user WHERE user LIKE '%git%'; - -# If git user exists and gitlab user does not exist -# you are done with the database cleanup tasks -mysql> \q - -# If both users exist skip to Delete gitlab user - -# Create new user for GitLab (changed in GitLab 6.4) -# change $password in the command below to a real password you pick -mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; - -# Grant the git user necessary permissions on the database -mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlabhq_production`.* TO 'git'@'localhost'; - -# Delete the old gitlab user -mysql> DELETE FROM mysql.user WHERE user='gitlab'; - -# Quit the database session -mysql> \q - -# Try connecting to the new database with the new user -sudo -u git -H mysql -u git -p -D gitlabhq_production - -# Type the password you replaced $password with earlier - -# You should now see a 'mysql>' prompt - -# Quit the database session -mysql> \q - -# Update database configuration details -# See config/database.yml.mysql for latest recommended configuration details -# Remove the reaping_frequency setting line if it exists (removed in GitLab 6.8) -# Set production -> pool: 10 (updated in GitLab 5.3) -# Set production -> username: git -# Set production -> password: the password your replaced $password with earlier -sudo -u git -H editor /home/git/gitlab/config/database.yml - -## Things went south? Revert to previous version (6.0) - -### 1. Revert the code to the previous version - -Follow the [upgrade guide from 5.4 to 6.0](5.4-to-6.0.md), except for the database migration (the backup is already migrated to the previous version). - -### 2. Restore from the backup: - -```bash -cd /home/git/gitlab -sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production -``` - -## Login issues after upgrade? - -If running in HTTPS mode, be sure to read [Can't Verify CSRF token authenticity](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide#cant-verify-csrf-token-authenticitycant-get-past-login-pageredirected-to-login-page) -- cgit v1.2.1 From 3b6737f970eec80d82ddc78d098b3b3fccbe4acf Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 15:08:37 +0100 Subject: Add interceptor test. --- spec/lib/disable_email_interceptor_spec.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 spec/lib/disable_email_interceptor_spec.rb diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb new file mode 100644 index 00000000000..29ec54b13d1 --- /dev/null +++ b/spec/lib/disable_email_interceptor_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe DisableEmailInterceptor do + before do + ActionMailer::Base.register_interceptor(DisableEmailInterceptor) + end + + it 'should not send emails' do + Gitlab.config.gitlab.stub(:email_enabled).and_return(false) + expect { + deliver_mail + }.not_to change(ActionMailer::Base.deliveries, :count) + end + + after do + Mail.class_variable_set(:@@delivery_interceptors, []) + end + + def deliver_mail + key = create :personal_key + Notify.new_ssh_key_email(key.id) + end +end -- cgit v1.2.1 From 28c08775b3d1994d3a8c5057534c704ff9da4bae Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 15:11:03 +0100 Subject: Add a comment in interceptor spec. --- spec/lib/disable_email_interceptor_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb index 29ec54b13d1..8bf6ee2ed50 100644 --- a/spec/lib/disable_email_interceptor_spec.rb +++ b/spec/lib/disable_email_interceptor_spec.rb @@ -13,6 +13,9 @@ describe DisableEmailInterceptor do end after do + # Removing interceptor from the list because unregister_interceptor is + # implemented in later version of mail gem + # See: https://github.com/mikel/mail/pull/705 Mail.class_variable_set(:@@delivery_interceptors, []) end -- cgit v1.2.1 From d08bb4b3a467d730009a97c79573854af79147d6 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 27 Oct 2014 16:08:11 +0100 Subject: Add project name to rename repository section --- app/views/projects/edit.html.haml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index f48f4bb2953..79be310c2cf 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -13,7 +13,11 @@ = f.label :name, class: 'control-label' do Project name .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control" + = f.text_field :name, placeholder: "Example Project", class: "form-control", readonly: true + %p.hint + Rename the project at + %strong Rename repository + section. .form-group @@ -124,6 +128,12 @@ .errors-holder .panel-body = form_for(@project, html: { class: 'form-horizontal' }) do |f| + .form-group.project_name_holder + = f.label :name, class: 'control-label' do + Project name + .col-sm-9 + .form-group + = f.text_field :name, placeholder: "Example Project", class: "form-control" .form-group = f.label :path, class: 'control-label' do %span Path -- cgit v1.2.1 From 2d187976be893a764aea9699e6b5a1cb15c34d84 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Wed, 22 Oct 2014 00:19:16 +0200 Subject: Run user select Js only where needed Transform current implementation into regular Coffescript classes so that the same call method can be reused on the dispatcher as for other classes. --- app/assets/javascripts/dispatcher.js.coffee | 6 +++ .../javascripts/project_users_select.js.coffee | 19 ++++---- app/assets/javascripts/users_select.js.coffee | 53 ++++++++++++---------- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index fb0560dba49..b070aba2ef4 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -62,6 +62,7 @@ class Dispatcher new TeamMembers() when 'groups:members' new GroupMembers() + new UsersSelect() when 'groups:new', 'groups:edit', 'admin:groups:edit' new GroupAvatar() when 'projects:tree:show' @@ -81,6 +82,8 @@ class Dispatcher when 'admin' new Admin() switch path[1] + when 'groups' + new UsersSelect() when 'projects' new NamespaceSelect() when 'dashboard' @@ -95,6 +98,8 @@ class Dispatcher new ProjectNew() when 'show' new ProjectShow() + when 'issues', 'merge_requests' + new ProjectUsersSelect() when 'wikis' new Wikis() shortcut_handler = new ShortcutsNavigation() @@ -103,6 +108,7 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' shortcut_handler = new ShortcutsNavigation() + new UsersSelect() # If we haven't installed a custom shortcut handler, install the default one diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee index cfbcd5108c8..7fb33926096 100644 --- a/app/assets/javascripts/project_users_select.js.coffee +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -1,6 +1,6 @@ -@projectUsersSelect = - init: -> - $('.ajax-project-users-select').each (i, select) -> +class @ProjectUsersSelect + constructor: -> + $('.ajax-project-users-select').each (i, select) => project_id = $(select).data('project-id') || $('body').data('project-id') $(select).select2 @@ -28,14 +28,16 @@ Api.user(id, callback) - formatResult: projectUsersSelect.projectUserFormatResult - formatSelection: projectUsersSelect.projectUserFormatSelection + formatResult: (args...) => + @formatResult(args...) + formatSelection: (args...) => + @formatSelection(args...) dropdownCssClass: "ajax-project-users-dropdown" dropdownAutoWidth: true escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results m - projectUserFormatResult: (user) -> + formatResult: (user) -> if user.avatar_url avatar = user.avatar_url else @@ -52,8 +54,5 @@
    #{user.username}
    " - projectUserFormatSelection: (user) -> + formatSelection: (user) -> user.name - -$ -> - projectUsersSelect.init() diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 86318bd7d94..9eee7406511 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,5 +1,30 @@ -$ -> - userFormatResult = (user) -> +class @UsersSelect + constructor: -> + $('.ajax-users-select').each (i, select) => + $(select).select2 + placeholder: "Search for a user" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) -> + Api.users query.term, (users) -> + data = { results: users } + query.callback(data) + + initSelection: (element, callback) -> + id = $(element).val() + if id isnt "" + Api.user(id, callback) + + + formatResult: (args...) => + @formatResult(args...) + formatSelection: (args...) => + @formatSelection(args...) + dropdownCssClass: "ajax-users-dropdown" + escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results + m + + formatResult: (user) -> if user.avatar_url avatar = user.avatar_url else @@ -11,27 +36,5 @@ $ ->
    #{user.username}
    " - userFormatSelection = (user) -> + formatSelection: (user) -> user.name - - $('.ajax-users-select').each (i, select) -> - $(select).select2 - placeholder: "Search for a user" - multiple: $(select).hasClass('multiselect') - minimumInputLength: 0 - query: (query) -> - Api.users query.term, (users) -> - data = { results: users } - query.callback(data) - - initSelection: (element, callback) -> - id = $(element).val() - if id isnt "" - Api.user(id, callback) - - - formatResult: userFormatResult - formatSelection: userFormatSelection - dropdownCssClass: "ajax-users-dropdown" - escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results - m -- cgit v1.2.1 From 50ee0b81b8ade93a919a78ef1c04dc0afd6f3074 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 28 Oct 2014 09:19:58 +0100 Subject: Leave the project name field editable, fix the test. --- app/views/projects/edit.html.haml | 6 +----- features/steps/project/project.rb | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 79be310c2cf..b85cf7d8d37 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -13,11 +13,7 @@ = f.label :name, class: 'control-label' do Project name .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control", readonly: true - %p.hint - Rename the project at - %strong Rename repository - section. + = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit" .form-group diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index f7fff8e64f9..5e7312d90ff 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -4,7 +4,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps include SharedPaths step 'change project settings' do - fill_in 'project_name', with: 'NewName' + fill_in 'project_name_edit', with: 'NewName' uncheck 'project_issues_enabled' end -- cgit v1.2.1 From 776bca07cd4b3996a39035e90232c99599ed2663 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 16 Oct 2014 11:34:19 -0500 Subject: Add Atlassian Bamboo service --- CHANGELOG | 1 + app/controllers/projects/services_controller.rb | 3 +- app/models/project.rb | 3 +- app/models/project_services/bamboo_service.rb | 105 ++++++++++++++++++++++++ app/views/projects/services/_form.html.haml | 4 +- doc/README.md | 1 + doc/project_services/bamboo.md | 60 ++++++++++++++ doc/project_services/project_services.md | 18 ++++ features/project/service.feature | 6 ++ features/steps/project/services.rb | 20 +++++ 10 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 app/models/project_services/bamboo_service.rb create mode 100644 doc/project_services/bamboo.md create mode 100644 doc/project_services/project_services.md diff --git a/CHANGELOG b/CHANGELOG index 01ae3562ded..f01267c460c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ v 7.5.0 - Add time zone configuration on gitlab.yml (Sullivan Senechal) - Fix LDAP authentication for Git HTTP access - Fix LDAP config lookup for provider 'ldap' + - Add Atlassian Bamboo CI service (Drew Blessing) v 7.4.2 - Fix internal snippet exposing for unauthenticated users diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index b50f6286459..a5f30dcfd9d 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController params.require(:service).permit( :title, :token, :type, :active, :api_key, :subdomain, :room, :recipients, :project_url, :webhook, - :user_key, :device, :priority, :sound + :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, + :build_key ) end end diff --git a/app/models/project.rb b/app/models/project.rb index 613f98ba44b..c58c9b551c9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -65,6 +65,7 @@ class Project < ActiveRecord::Base has_one :gemnasium_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildbox_service, dependent: :destroy + has_one :bamboo_service, dependent: :destroy has_one :pushover_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link @@ -313,7 +314,7 @@ class Project < ActiveRecord::Base end def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox) + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox bamboo) end def gitlab_ci? diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb new file mode 100644 index 00000000000..b9eec9ab21e --- /dev/null +++ b/app/models/project_services/bamboo_service.rb @@ -0,0 +1,105 @@ +class BambooService < CiService + include HTTParty + + prop_accessor :bamboo_url, :build_key, :username, :password + + validates :bamboo_url, presence: true, + format: { with: URI::regexp }, if: :activated? + validates :build_key, presence: true, if: :activated? + validates :username, presence: true, + if: ->(service) { service.password? }, if: :activated? + validates :password, presence: true, + if: ->(service) { service.username? }, if: :activated? + + attr_accessor :response + + after_save :compose_service_hook, if: :activated? + + def compose_service_hook + hook = service_hook || build_service_hook + hook.save + end + + def title + 'Atlassian Bamboo CI' + end + + def description + 'A continuous integration and build server' + end + + def help + 'You must set up automatic revision labeling and a repository trigger in Bamboo.' + end + + def to_param + 'bamboo' + end + + def fields + [ + { type: 'text', name: 'bamboo_url', + placeholder: 'Bamboo root URL like https://bamboo.example.com' }, + { type: 'text', name: 'build_key', + placeholder: 'Bamboo build plan key like KEY' }, + { type: 'text', name: 'username', + placeholder: 'A user with API access, if applicable' }, + { type: 'password', name: 'password' }, + ] + end + + def build_info(sha) + url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") + + if username.blank? && password.blank? + @response = HTTParty.get(parsed_url.to_s, verify: false) + else + get_url = "#{url}&os_authType=basic" + auth = { + username: username, + password: password, + } + @response = HTTParty.get(get_url, verify: false, basic_auth: auth) + end + end + + def build_page(sha) + build_info(sha) if @response.nil? || !@response.code + + if @response.code != 200 || @response['results']['results']['size'] == '0' + # If actual build link can't be determined, send user to build summary page. + "#{bamboo_url}/browse/#{build_key}" + else + # If actual build link is available, go to build result page. + result_key = @response['results']['results']['result']['planResultKey']['key'] + "#{bamboo_url}/browse/#{result_key}" + end + end + + def commit_status(sha) + build_info(sha) if @response.nil? || !@response.code + return :error unless @response.code == 200 || @response.code == 404 + + status = if @response.code == 404 || @response['results']['results']['size'] == '0' + 'Pending' + else + @response['results']['results']['result']['buildState'] + end + + if status.include?('Success') + 'success' + elsif status.include?('Failed') + 'failed' + elsif status.include?('Pending') + 'pending' + else + :error + end + end + + def execute(_data) + # Bamboo requires a GET and does not take any data. + self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}", + verify: false) + end +end diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 16d59d1fe9d..1151f22c7e8 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -28,7 +28,7 @@ - @service.fields.each do |field| - name = field[:name] - - value = @service.send(name) + - value = @service.send(name) unless field[:type] == 'password' - type = field[:type] - placeholder = field[:placeholder] - choices = field[:choices] @@ -45,6 +45,8 @@ = f.check_box name - elsif type == 'select' = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } + - elsif type == 'password' + = f.password_field name, class: 'form-control' .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/doc/README.md b/doc/README.md index a8e21f75714..7343d5ae273 100644 --- a/doc/README.md +++ b/doc/README.md @@ -5,6 +5,7 @@ - [API](api/README.md) Explore how you can access GitLab via a simple and powerful API. - [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system. - [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do. +- [Project Services](project_services/project_services.md) Explore how project services can integrate a project with external services, such as for CI. - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. diff --git a/doc/project_services/bamboo.md b/doc/project_services/bamboo.md new file mode 100644 index 00000000000..51668128c62 --- /dev/null +++ b/doc/project_services/bamboo.md @@ -0,0 +1,60 @@ +# Atlassian Bamboo CI Service + +GitLab provides integration with Atlassian Bamboo for continuous integration. +When configured, pushes to a project will trigger a build in Bamboo automatically. +Merge requests will also display CI status showing whether the build is pending, +failed, or completed successfully. It also provides a link to the Bamboo build +page for more information. + +Bamboo doesn't quite provide the same features as a traditional build system when +it comes to accepting webhooks and commit data. There are a few things that +need to be configured in a Bamboo build plan before GitLab can integrate. + +## Setup + +### Complete these steps in Bamboo: + +1. Navigate to a Bamboo build plan and choose 'Configure plan' from the 'Actions' +dropdown. +1. Select the 'Triggers' tab. +1. Click 'Add trigger'. +1. Enter a description such as 'GitLab trigger' +1. Choose 'Repository triggers the build when changes are committed' +1. Check one or more repositories checkboxes +1. Enter the GitLab IP address in the 'Trigger IP addresses' box. This is a +whitelist of IP addresses that are allowed to trigger Bamboo builds. +1. Save the trigger. +1. In the left pane, select a build stage. If you have multiple build stages +you want to select the last stage that contains the git checkout task. +1. Select the 'Miscellaneous' tab. +1. Under 'Pattern Match Labelling' put '${bamboo.repository.revision.number}' +in the 'Labels' box. +1. Save + +Bamboo is now ready to accept triggers from GitLab. Next, set up the Bamboo +service in GitLab + +### Complete these steps in GitLab: + +1. Navigate to the project you want to configure to trigger builds. +1. Select 'Settings' in the top navigation. +1. Select 'Services' in the left navigation. +1. Click 'Atlassian Bamboo CI' +1. Select the 'Active' checkbox. +1. Enter the base URL of your Bamboo server. 'https://bamboo.example.com' +1. Enter the build key from your Bamboo build plan. Build keys are a short, +all capital letter, identifier that is unique. It will be something like PR-BLD +1. If necessary, enter username and password for a Bamboo user that has +access to trigger the build plan. Leave these fields blank if you do not require +authentication. +1. Save or optionally click 'Test Settings'. Please note that 'Test Settings' +will actually trigger a build in Bamboo. + +## Troubleshooting + +If builds are not triggered, these are a couple of things to keep in mind. + +1. Ensure you entered the right GitLab IP address in Bamboo under 'Trigger +IP addresses'. +1. Remember that GitLab only triggers builds on push events. A commit via the +web interface will not trigger CI currently. diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md new file mode 100644 index 00000000000..20a69a211dd --- /dev/null +++ b/doc/project_services/project_services.md @@ -0,0 +1,18 @@ +# Project Services + +__Project integrations with external services for continuous integration and more.__ + +## Services + +- Assemblia +- [Atlassian Bamboo CI](bamboo.md) An Atlassian product for continous integration. +- Build box +- Campfire +- Emails on push +- Flowdock +- Gemnasium +- GitLab CI +- Hipchat +- PivotalTracker +- Pushover +- Slack diff --git a/features/project/service.feature b/features/project/service.feature index af88eaefa8f..88fd038d45f 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -54,3 +54,9 @@ Feature: Project Services And I click email on push service link And I fill email on push settings Then I should see email on push service settings saved + + Scenario: Activate Atlassian Bamboo CI service + When I visit project "Shop" services page + And I click Atlassian Bamboo CI service link + And I fill Atlassian Bamboo CI settings + Then I should see Atlassian Bamboo CI service settings saved diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 5bd60f99c84..17d62210d10 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -14,6 +14,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps page.should have_content 'GitLab CI' page.should have_content 'Assembla' page.should have_content 'Pushover' + page.should have_content 'Atlassian Bamboo' end step 'I click gitlab-ci service link' do @@ -137,4 +138,23 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps find_field('Priority').find('option[selected]').value.should == '1' find_field('Sound').find('option[selected]').value.should == 'bike' end + + step 'I click Atlassian Bamboo CI service link' do + click_link 'Atlassian Bamboo CI' + end + + step 'I fill Atlassian Bamboo CI settings' do + check 'Active' + fill_in 'Bamboo url', with: 'http://bamboo.example.com' + fill_in 'Build key', with: 'KEY' + fill_in 'Username', with: 'user' + fill_in 'Password', with: 'verySecret' + click_button 'Save' + end + + step 'I should see Atlassian Bamboo CI service settings saved' do + find_field('Bamboo url').value.should == 'http://bamboo.example.com' + find_field('Build key').value.should == 'KEY' + find_field('Username').value.should == 'user' + end end -- cgit v1.2.1 From f3c120d25a7c9ab31609f93a0549c5174c519baf Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 28 Oct 2014 16:00:03 +0200 Subject: Add failing test that should be green after group members api get fixed Signed-off-by: Dmitriy Zaporozhets --- spec/requests/api/groups_spec.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 42ccad71aaf..f56caeaf5ad 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -220,13 +220,27 @@ describe API::API, api: true do context "when a member of the group" do it "should return ok and add new member" do - count_before=group_no_members.group_members.count new_user = create(:user) - post api("/groups/#{group_no_members.id}/members", owner), user_id: new_user.id, access_level: GroupMember::MASTER + + expect { + post api("/groups/#{group_no_members.id}/members", owner), + user_id: new_user.id, access_level: GroupMember::MASTER + }.to change { group_no_members.members.count }.by(1) + response.status.should == 201 json_response['name'].should == new_user.name json_response['access_level'].should == GroupMember::MASTER - group_no_members.group_members.count.should == count_before + 1 + end + + it "should not allow guest to modify group members" do + new_user = create(:user) + + expect { + post api("/groups/#{group_with_members.id}/members", guest), + user_id: new_user.id, access_level: GroupMember::MASTER + }.not_to change { group_with_members.members.count } + + response.status.should == 403 end it "should return error if member already exists" do -- cgit v1.2.1 From 9397d19380aa4f63c9f8b67bf06b9d9ca3db3c1a Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Tue, 28 Oct 2014 11:02:36 -0500 Subject: Added ID to the form-group, to fix alignment of inputs --- app/views/devise/passwords/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 182ca5e774b..f6cbf9b82ba 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -6,7 +6,7 @@ .devise-errors = devise_error_messages! = f.hidden_field :reset_password_token - .form-group + .form-group#password-strength = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true -- cgit v1.2.1 From bf07fcf06ad7a5d2fd5f25079ce9e16d003481c7 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Tue, 28 Oct 2014 11:04:46 -0500 Subject: Removed unnecessary role in form. --- app/views/devise/registrations/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 806d206d7b9..123de881f59 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -2,7 +2,7 @@ .login-heading %h3 Sign up .login-body - = form_for(resource, as: resource_name, url: registration_path(resource_name), role: 'form') do |f| + = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| .devise-errors = devise_error_messages! %div -- cgit v1.2.1 From dc4caa26bf438ec9193efd445d55dd0ad79cc906 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Tue, 28 Oct 2014 09:47:57 -0700 Subject: minor requirements.md cleanup * "Non Unix"->"Non-Unix" * Fix poor wording --- doc/install/requirements.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 85fb260c96e..ed194253148 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -24,7 +24,7 @@ For the installations options please see [the installation page on the GitLab we On the above unsupported distributions is still possible to install GitLab yourself. Please see the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information. -### Non Unix operating systems such as Windows +### Non-Unix operating systems such as Windows GitLab is developed for Unix operating systems. GitLab does **not** run on Windows and we have no plans of supporting it in the near future. @@ -53,8 +53,8 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab - 512MB is the absolute minimum but we do not recommend this amount of memory. You will either need to configure 512MB or 1.5GB of swap space. With 512MB of swap space you must configure only one unicorn worker. -With one unicorn worker only git over ssh access will work because the git over http access requires two running workers (one worker to receive the user request and one worker for the authorization check). -If you use SSD storage and configure 1.5GB of swap space you can use two Unicorn workers, this will allow http access but it will still be slow. +With one unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). +If you use SSD storage and configure 1.5GB of swap space you can use two Unicorn workers, this will allow HTTP access but it will still be slow. - 1GB RAM + 1GB swap supports up to 100 users - **2GB RAM** is the **recommended** memory size and supports up to 500 users - 4GB RAM supports up to 2,000 users @@ -67,7 +67,7 @@ Notice: The 25 workers of Sidekiq will show up as separate processes in your pro ### Storage -The necessary hard drive space largely depends on the size of the repos you want to store in GitLab. But as a *rule of thumb* you should have at least twice as much free space as your all repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. +The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them. -- cgit v1.2.1 From d9023da90d3d723bcb9ba372b0b89a8747aca6af Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Tue, 28 Oct 2014 12:48:20 -0500 Subject: Refactored password_strength configuration --- app/assets/javascripts/password_strength.js.coffee | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee index 33b4d2e0f69..825f5630266 100644 --- a/app/assets/javascripts/password_strength.js.coffee +++ b/app/assets/javascripts/password_strength.js.coffee @@ -4,29 +4,25 @@ overwritten_messages = overwritten_rules = wordSequences: false + +options = + showProgressBar: false + showVerdicts: false + showPopover: true + showErrors: true + showStatus: true + errorMessages: overwritten_messages $(document).ready -> profileOptions = {} - profileOptions.ui = - showProgressBar: false - showVerdicts: false - showPopover: true - showErrors: true - showStatus: true - errorMessages: overwritten_messages + profileOptions.ui = options profileOptions.rules = activated: overwritten_rules deviseOptions = {} deviseOptions.common = usernameField: "#user_username" - deviseOptions.ui = - showPopover: true - showErrors: true - showVerdicts: false - showProgressBar: false - showStatus: true - errorMessages: overwritten_messages + deviseOptions.ui = options deviseOptions.rules = activated: overwritten_rules -- cgit v1.2.1 From fa39863611a3eab3f386ccd0e8e5003cbd9a7ddb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 28 Oct 2014 20:02:01 +0200 Subject: Fix tests Signed-off-by: Dmitriy Zaporozhets --- spec/features/projects_spec.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 87bfe102d39..d291621935b 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Projects", feature: true do +describe "Projects", feature: true, js: true do before { login_as :user } describe "DELETE /projects/:id" do @@ -10,21 +10,23 @@ describe "Projects", feature: true do visit edit_project_path(@project) end - it "should be correct path", js: true do - expect { - click_link "Remove project" - fill_in 'confirm_name_input', with: @project.path - click_button 'Confirm' - }.to change {Project.count}.by(-1) + it "should remove project" do + expect { remove_project }.to change {Project.count}.by(-1) end - it 'should delete the project from the database and disk' do + it 'should delete the project from disk' do expect(GitlabShellWorker).to( receive(:perform_async).with(:remove_repository, /#{@project.path_with_namespace}/) ).twice - expect { click_link "Remove project" }.to change {Project.count}.by(-1) + remove_project end end + + def remove_project + click_link "Remove project" + fill_in 'confirm_name_input', with: @project.path + click_button 'Confirm' + end end -- cgit v1.2.1 From f6491508fe54c75c8db8db17b27d6d7912198a7a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 29 Oct 2014 13:31:23 +0200 Subject: Split group members api Signed-off-by: Dmitriy Zaporozhets --- lib/api/api.rb | 1 + lib/api/group_members.rb | 74 ++++++++++++++++++ lib/api/groups.rb | 51 ------------- spec/requests/api/group_members_spec.rb | 130 ++++++++++++++++++++++++++++++++ spec/requests/api/groups_spec.rb | 124 ------------------------------ 5 files changed, 205 insertions(+), 175 deletions(-) create mode 100644 lib/api/group_members.rb create mode 100644 spec/requests/api/group_members_spec.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index 2c7cd9038c3..d26667ba3f7 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -27,6 +27,7 @@ module API helpers APIHelpers mount Groups + mount GroupMembers mount Users mount Projects mount Repositories diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb new file mode 100644 index 00000000000..24c141e9b71 --- /dev/null +++ b/lib/api/group_members.rb @@ -0,0 +1,74 @@ +module API + class GroupMembers < Grape::API + before { authenticate! } + + resource :groups do + helpers do + def find_group(id) + group = Group.find(id) + + if can?(current_user, :read_group, group) + group + else + render_api_error!("403 Forbidden - #{current_user.username} lacks sufficient access to #{group.name}", 403) + end + end + + def validate_access_level?(level) + Gitlab::Access.options_with_owner.values.include? level.to_i + end + end + + # Get a list of group members viewable by the authenticated user. + # + # Example Request: + # GET /groups/:id/members + get ":id/members" do + group = find_group(params[:id]) + members = group.group_members + users = (paginate members).collect(&:user) + present users, with: Entities::GroupMember, group: group + end + + # Add a user to the list of group members + # + # Parameters: + # id (required) - group id + # user_id (required) - the users id + # access_level (required) - Project access level + # Example Request: + # POST /groups/:id/members + post ":id/members" do + required_attributes! [:user_id, :access_level] + unless validate_access_level?(params[:access_level]) + render_api_error!("Wrong access level", 422) + end + group = find_group(params[:id]) + if group.group_members.find_by(user_id: params[:user_id]) + render_api_error!("Already exists", 409) + end + group.add_users([params[:user_id]], params[:access_level]) + member = group.group_members.find_by(user_id: params[:user_id]) + present member.user, with: Entities::GroupMember, group: group + end + + # Remove member. + # + # Parameters: + # id (required) - group id + # user_id (required) - the users id + # + # Example Request: + # DELETE /groups/:id/members/:user_id + delete ":id/members/:user_id" do + group = find_group(params[:id]) + member = group.group_members.find_by(user_id: params[:user_id]) + if member.nil? + render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404) + else + member.destroy + end + end + end + end +end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 4841e04689d..f0ab6938b1c 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -97,57 +97,6 @@ module API not_found! end end - - # Get a list of group members viewable by the authenticated user. - # - # Example Request: - # GET /groups/:id/members - get ":id/members" do - group = find_group(params[:id]) - members = group.group_members - users = (paginate members).collect(&:user) - present users, with: Entities::GroupMember, group: group - end - - # Add a user to the list of group members - # - # Parameters: - # id (required) - group id - # user_id (required) - the users id - # access_level (required) - Project access level - # Example Request: - # POST /groups/:id/members - post ":id/members" do - required_attributes! [:user_id, :access_level] - unless validate_access_level?(params[:access_level]) - render_api_error!("Wrong access level", 422) - end - group = find_group(params[:id]) - if group.group_members.find_by(user_id: params[:user_id]) - render_api_error!("Already exists", 409) - end - group.add_users([params[:user_id]], params[:access_level]) - member = group.group_members.find_by(user_id: params[:user_id]) - present member.user, with: Entities::GroupMember, group: group - end - - # Remove member. - # - # Parameters: - # id (required) - group id - # user_id (required) - the users id - # - # Example Request: - # DELETE /groups/:id/members/:user_id - delete ":id/members/:user_id" do - group = find_group(params[:id]) - member = group.group_members.find_by(user_id: params[:user_id]) - if member.nil? - render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404) - else - member.destroy - end - end end end end diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb new file mode 100644 index 00000000000..b266f56a9dd --- /dev/null +++ b/spec/requests/api/group_members_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + + let(:owner) { create(:user) } + let(:reporter) { create(:user) } + let(:developer) { create(:user) } + let(:master) { create(:user) } + let(:guest) { create(:user) } + let(:stranger) { create(:user) } + + let!(:group_with_members) do + group = create(:group) + group.add_users([reporter.id], GroupMember::REPORTER) + group.add_users([developer.id], GroupMember::DEVELOPER) + group.add_users([master.id], GroupMember::MASTER) + group.add_users([guest.id], GroupMember::GUEST) + group + end + + let!(:group_no_members) { create(:group) } + + before do + group_with_members.add_owner owner + group_no_members.add_owner owner + end + + describe "GET /groups/:id/members" do + context "when authenticated as user that is part or the group" do + it "each user: should return an array of members groups of group3" do + [owner, master, developer, reporter, guest].each do |user| + get api("/groups/#{group_with_members.id}/members", user) + response.status.should == 200 + json_response.should be_an Array + json_response.size.should == 5 + json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER + json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER + json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER + json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER + json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST + end + end + + it "users not part of the group should get access error" do + get api("/groups/#{group_with_members.id}/members", stranger) + response.status.should == 403 + end + end + end + + describe "POST /groups/:id/members" do + context "when not a member of the group" do + it "should not add guest as member of group_no_members when adding being done by person outside the group" do + post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER + response.status.should == 403 + end + end + + context "when a member of the group" do + it "should return ok and add new member" do + new_user = create(:user) + + expect { + post api("/groups/#{group_no_members.id}/members", owner), + user_id: new_user.id, access_level: GroupMember::MASTER + }.to change { group_no_members.members.count }.by(1) + + response.status.should == 201 + json_response['name'].should == new_user.name + json_response['access_level'].should == GroupMember::MASTER + end + + it "should not allow guest to modify group members" do + new_user = create(:user) + + expect { + post api("/groups/#{group_with_members.id}/members", guest), + user_id: new_user.id, access_level: GroupMember::MASTER + }.not_to change { group_with_members.members.count } + + response.status.should == 403 + end + + it "should return error if member already exists" do + post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER + response.status.should == 409 + end + + it "should return a 400 error when user id is not given" do + post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER + response.status.should == 400 + end + + it "should return a 400 error when access level is not given" do + post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id + response.status.should == 400 + end + + it "should return a 422 error when access level is not known" do + post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234 + response.status.should == 422 + end + end + end + + describe "DELETE /groups/:id/members/:user_id" do + context "when not a member of the group" do + it "should not delete guest's membership of group_with_members" do + random_user = create(:user) + delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user) + response.status.should == 403 + end + end + + context "when a member of the group" do + it "should delete guest's membership of group" do + count_before=group_with_members.group_members.count + delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) + response.status.should == 200 + group_with_members.group_members.count.should == count_before - 1 + end + + it "should return a 404 error when user id is not known" do + delete api("/groups/#{group_with_members.id}/members/1328", owner) + response.status.should == 404 + end + end + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index f56caeaf5ad..8dfd2cd650e 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -165,128 +165,4 @@ describe API::API, api: true do end end end - - describe "members" do - let(:owner) { create(:user) } - let(:reporter) { create(:user) } - let(:developer) { create(:user) } - let(:master) { create(:user) } - let(:guest) { create(:user) } - let!(:group_with_members) do - group = create(:group) - group.add_users([reporter.id], GroupMember::REPORTER) - group.add_users([developer.id], GroupMember::DEVELOPER) - group.add_users([master.id], GroupMember::MASTER) - group.add_users([guest.id], GroupMember::GUEST) - group - end - let!(:group_no_members) { create(:group) } - - before do - group_with_members.add_owner owner - group_no_members.add_owner owner - end - - describe "GET /groups/:id/members" do - context "when authenticated as user that is part or the group" do - it "each user: should return an array of members groups of group3" do - [owner, master, developer, reporter, guest].each do |user| - get api("/groups/#{group_with_members.id}/members", user) - response.status.should == 200 - json_response.should be_an Array - json_response.size.should == 5 - json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER - json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER - json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER - json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER - json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST - end - end - - it "users not part of the group should get access error" do - get api("/groups/#{group_with_members.id}/members", user1) - response.status.should == 403 - end - end - end - - describe "POST /groups/:id/members" do - context "when not a member of the group" do - it "should not add guest as member of group_no_members when adding being done by person outside the group" do - post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER - response.status.should == 403 - end - end - - context "when a member of the group" do - it "should return ok and add new member" do - new_user = create(:user) - - expect { - post api("/groups/#{group_no_members.id}/members", owner), - user_id: new_user.id, access_level: GroupMember::MASTER - }.to change { group_no_members.members.count }.by(1) - - response.status.should == 201 - json_response['name'].should == new_user.name - json_response['access_level'].should == GroupMember::MASTER - end - - it "should not allow guest to modify group members" do - new_user = create(:user) - - expect { - post api("/groups/#{group_with_members.id}/members", guest), - user_id: new_user.id, access_level: GroupMember::MASTER - }.not_to change { group_with_members.members.count } - - response.status.should == 403 - end - - it "should return error if member already exists" do - post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER - response.status.should == 409 - end - - it "should return a 400 error when user id is not given" do - post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER - response.status.should == 400 - end - - it "should return a 400 error when access level is not given" do - post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id - response.status.should == 400 - end - - it "should return a 422 error when access level is not known" do - post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234 - response.status.should == 422 - end - end - end - - describe "DELETE /groups/:id/members/:user_id" do - context "when not a member of the group" do - it "should not delete guest's membership of group_with_members" do - random_user = create(:user) - delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user) - response.status.should == 403 - end - end - - context "when a member of the group" do - it "should delete guest's membership of group" do - count_before=group_with_members.group_members.count - delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) - response.status.should == 200 - group_with_members.group_members.count.should == count_before - 1 - end - - it "should return a 404 error when user id is not known" do - delete api("/groups/#{group_with_members.id}/members/1328", owner) - response.status.should == 404 - end - end - end - end end -- cgit v1.2.1 From eea6a8a17deb384bfe6b9b83462df6cb5f7f17ad Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 29 Oct 2014 13:38:00 +0200 Subject: Dont allow guests..developers to manage group members Signed-off-by: Dmitriy Zaporozhets --- lib/api/group_members.rb | 10 ++++++++-- spec/requests/api/group_members_spec.rb | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb index 24c141e9b71..d596517c816 100644 --- a/lib/api/group_members.rb +++ b/lib/api/group_members.rb @@ -39,14 +39,18 @@ module API # Example Request: # POST /groups/:id/members post ":id/members" do + group = find_group(params[:id]) + authorize! :manage_group, group required_attributes! [:user_id, :access_level] + unless validate_access_level?(params[:access_level]) render_api_error!("Wrong access level", 422) end - group = find_group(params[:id]) + if group.group_members.find_by(user_id: params[:user_id]) render_api_error!("Already exists", 409) end + group.add_users([params[:user_id]], params[:access_level]) member = group.group_members.find_by(user_id: params[:user_id]) present member.user, with: Entities::GroupMember, group: group @@ -62,7 +66,9 @@ module API # DELETE /groups/:id/members/:user_id delete ":id/members/:user_id" do group = find_group(params[:id]) - member = group.group_members.find_by(user_id: params[:user_id]) + authorize! :manage_group, group + member = group.group_members.find_by(user_id: params[:user_id]) + if member.nil? render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404) else diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb index b266f56a9dd..4957186f605 100644 --- a/spec/requests/api/group_members_spec.rb +++ b/spec/requests/api/group_members_spec.rb @@ -115,16 +115,22 @@ describe API::API, api: true do context "when a member of the group" do it "should delete guest's membership of group" do - count_before=group_with_members.group_members.count - delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) + expect { + delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) + }.to change { group_with_members.members.count }.by(-1) + response.status.should == 200 - group_with_members.group_members.count.should == count_before - 1 end it "should return a 404 error when user id is not known" do delete api("/groups/#{group_with_members.id}/members/1328", owner) response.status.should == 404 end + + it "should not allow guest to modify group members" do + delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest) + response.status.should == 403 + end end end end -- cgit v1.2.1 From ec5482a0dc460ec8d6f25ca6fd40377aba2d7714 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 29 Oct 2014 16:34:28 +0200 Subject: Explicitly require addressable gem feature you need. Fixes Buildbox CI integration Signed-off-by: Dmitriy Zaporozhets --- app/models/project_services/buildbox_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb index b0f8e28c97f..0ab67b79fe4 100644 --- a/app/models/project_services/buildbox_service.rb +++ b/app/models/project_services/buildbox_service.rb @@ -12,6 +12,8 @@ # properties :text # +require "addressable/uri" + class BuildboxService < CiService prop_accessor :project_url, :token -- cgit v1.2.1 From 9cadfc9e134f720846bfa3d874d00545ecc78ac4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 29 Oct 2014 17:17:22 +0200 Subject: Update omniauth-ldap & dependencies Signed-off-by: Dmitriy Zaporozhets --- Gemfile | 2 +- Gemfile.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index f6f3607cbd1..0b6afc34355 100644 --- a/Gemfile +++ b/Gemfile @@ -37,7 +37,7 @@ gem "gitlab_git", '7.0.0.rc10' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' # LDAP Auth -gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap" +gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap" # Git Wiki gem 'gollum-lib', '~> 3.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 314884fa36e..6ca6179ca3c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,11 +185,11 @@ GEM gitlab-linguist (~> 3.0) rugged (~> 0.21.0) gitlab_meta (7.0) - gitlab_omniauth-ldap (1.1.0) - net-ldap (~> 0.7.0) + gitlab_omniauth-ldap (1.2.0) + net-ldap (~> 0.9) omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) - rubyntlm (~> 0.1.1) + rubyntlm (~> 0.3) gollum-lib (3.0.0) github-markup (~> 1.1.0) gitlab-grit (~> 2.6.5) @@ -299,7 +299,7 @@ GEM multi_xml (0.5.5) multipart-post (1.2.0) mysql2 (0.3.16) - net-ldap (0.7.0) + net-ldap (0.9.0) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.8.0) @@ -445,7 +445,7 @@ GEM rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) ruby-progressbar (1.2.0) - rubyntlm (0.1.1) + rubyntlm (0.4.0) rubypants (0.2.0) rugged (0.21.0) safe_yaml (0.9.7) @@ -626,7 +626,7 @@ DEPENDENCIES gitlab_emoji (~> 0.0.1.1) gitlab_git (= 7.0.0.rc10) gitlab_meta (= 7.0) - gitlab_omniauth-ldap (= 1.1.0) + gitlab_omniauth-ldap (= 1.2.0) gollum-lib (~> 3.0.0) gon (~> 5.0.0) grape (~> 0.6.1) -- cgit v1.2.1 From bd0d679b236aa03c5b1f4596d5d669e225288030 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Wed, 29 Oct 2014 16:24:07 -0700 Subject: Update patch manual to link to blog post. --- doc/release/patch.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/release/patch.md b/doc/release/patch.md index 3ee55028b1f..5d2fa053cac 100644 --- a/doc/release/patch.md +++ b/doc/release/patch.md @@ -26,6 +26,6 @@ Otherwise include it in the monthly release and note there was a regression fix 1. Apply the patch to GitLab Cloud and the private GitLab development server 1. [Build new packages with the latest version](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md) 1. Cherry-pick the changelog update back into master -1. Create blog post -1. Send tweets about the release from `@gitlabhq`, tweet should include the most important feature that the release is addressing as well as the link to the changelog +1. Create and publish a blog post +1. Send tweets about the release from `@gitlabhq`, tweet should include the most important feature that the release is addressing and link to the blog post 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only) -- cgit v1.2.1 From fd17ba9ffc3350c3054746eac4b6be1a84d690ac Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 29 Oct 2014 15:46:42 +0200 Subject: Mentioned users are not limited by project scope any more Signed-off-by: Dmitriy Zaporozhets --- app/models/concerns/mentionable.rb | 6 +----- app/services/notification_service.rb | 1 + spec/models/concerns/mentionable_spec.rb | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 spec/models/concerns/mentionable_spec.rb diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 5938d9cb28e..6c1aa99668a 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -52,11 +52,7 @@ module Mentionable if identifier == "all" users += project.team.members.flatten else - if has_project - id = project.team.members.find_by(username: identifier).try(:id) - else - id = User.find_by(username: identifier).try(:id) - end + id = User.find_by(username: identifier).try(:id) users << User.find(id) unless id.blank? end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 36781314278..c9a1574b84e 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -124,6 +124,7 @@ class NotificationService opts = { noteable_type: note.noteable_type, project_id: note.project_id } target = note.noteable + if target.respond_to?(:participants) recipients = target.participants else diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb new file mode 100644 index 00000000000..ca6f11b2a4d --- /dev/null +++ b/spec/models/concerns/mentionable_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe Issue, "Mentionable" do + describe :mentioned_users do + let!(:user) { create(:user, username: 'stranger') } + let!(:user2) { create(:user, username: 'john') } + let!(:issue) { create(:issue, description: '@stranger mentioned') } + + subject { issue.mentioned_users } + + it { should include(user) } + it { should_not include(user2) } + end +end -- cgit v1.2.1 From dade6650e1fe9ba13462957c15126d506e98673a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 30 Oct 2014 13:25:56 +0200 Subject: Add CHANGELOG entry Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index f01267c460c..1d1c6d26e11 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 7.5.0 - Fix LDAP authentication for Git HTTP access - 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 v 7.4.2 - Fix internal snippet exposing for unauthenticated users -- cgit v1.2.1 From 6cb5d3d2d4c88183a79c2587d7d1bcc4797c406e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 30 Oct 2014 14:44:29 +0200 Subject: Add addressable explicitly to Gemfile Signed-off-by: Dmitriy Zaporozhets --- Gemfile | 1 + Gemfile.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 0b6afc34355..a2314236e29 100644 --- a/Gemfile +++ b/Gemfile @@ -186,6 +186,7 @@ gem "gon", '~> 5.0.0' gem 'nprogress-rails' gem 'request_store' gem "virtus" +gem 'addressable' group :development do gem "annotate", "~> 2.6.0.beta2" diff --git a/Gemfile.lock b/Gemfile.lock index 6ca6179ca3c..800f33590cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -592,6 +592,7 @@ DEPENDENCIES RedCloth ace-rails-ap acts-as-taggable-on + addressable annotate (~> 2.6.0.beta2) asciidoctor (= 0.1.4) awesome_print -- cgit v1.2.1