diff --git a/CHANGELOG b/CHANGELOG index f26466f9da8..7d1ada97c57 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 5.3.0 - init.d: Ensure socket is removed before starting service - Admin area: Style teams:index, group:show pages - Own page for failed forking + - Scrum view for milestone v 5.2.0 - Turbolinks diff --git a/Gemfile b/Gemfile index e4206ab3d0d..0150ffb94ea 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ gem 'gitlab_git', '~> 1.3.0' gem 'gitlab-grack', '~> 1.0.1', require: 'grack' # LDAP Auth -gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" +gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap" # Syntax highlighter gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb' @@ -72,6 +72,9 @@ gem "seed-fu" gem "redcarpet", "~> 2.2.2" gem "github-markup", "~> 0.7.4", require: 'github/markup' +# Asciidoc to HTML +gem "asciidoctor" + # Servers gem "puma", '~> 2.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index b986539da1e..313d2ffbca5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,6 +46,7 @@ GEM rails (~> 3.0) addressable (2.3.4) arel (3.0.2) + asciidoctor (0.1.3) awesome_print (1.1.0) backports (2.6.7) bcrypt-ruby (3.0.1) @@ -168,8 +169,8 @@ GEM github-linguist (~> 2.3.4) gitlab-grit (~> 2.5.1) gitlab_meta (5.0) - gitlab_omniauth-ldap (1.0.2) - net-ldap (~> 0.2.2) + gitlab_omniauth-ldap (1.0.3) + net-ldap (~> 0.3.1) omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.1.1) @@ -265,7 +266,7 @@ GEM multi_xml (0.5.3) multipart-post (1.2.0) mysql2 (0.3.11) - net-ldap (0.2.2) + net-ldap (0.3.1) nokogiri (1.5.9) oauth (0.4.7) oauth2 (0.8.1) @@ -517,6 +518,7 @@ PLATFORMS DEPENDENCIES acts-as-taggable-on annotate! + asciidoctor awesome_print better_errors binding_of_caller @@ -544,7 +546,7 @@ DEPENDENCIES gitlab-pygments.rb (~> 0.3.2) gitlab_git (~> 1.3.0) gitlab_meta (= 5.0) - gitlab_omniauth-ldap (= 1.0.2) + gitlab_omniauth-ldap (= 1.0.3) gon grape (~> 0.4.1) grape-entity (~> 0.3.0) diff --git a/VERSION b/VERSION index 455f90e3b18..03f488b076a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.3.0.beta1 +5.3.0 diff --git a/app/assets/javascripts/milestones.js.coffee b/app/assets/javascripts/milestones.js.coffee deleted file mode 100644 index 99a52bf4d3f..00000000000 --- a/app/assets/javascripts/milestones.js.coffee +++ /dev/null @@ -1,14 +0,0 @@ -$ -> - $('.milestone-issue-filter li[data-closed]').addClass('hide') - - $('.milestone-issue-filter ul.nav li a').click -> - $('.milestone-issue-filter li').toggleClass('active') - $('.milestone-issue-filter li[data-closed]').toggleClass('hide') - false - - $('.milestone-merge-requests-filter li[data-closed]').addClass('hide') - - $('.milestone-merge-requests-filter ul.nav li a').click -> - $('.milestone-merge-requests-filter li').toggleClass('active') - $('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide') - false diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss index 0fc8b21de7b..db0023cff92 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/nav.scss @@ -57,6 +57,7 @@ border-color: #CCC; border-bottom: 1px solid #fff; color: #333; + font-weight: bold; } } } diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index e8680dde507..d057bcf669c 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -58,6 +58,7 @@ background: #f9f9f9; border-radius: 0; color: #555; + margin: 0 20px; } .note-file-attach { diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 9fe7a24b461..d4bb4872ac7 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -92,6 +92,10 @@ ul.notes { .note-body { @include md-typography; margin-left: 45px; + + .highlight { + @include border-radius(4px); + } } .note-header { padding-bottom: 5px; diff --git a/app/contexts/issues/list_context.rb b/app/contexts/issues/list_context.rb index a35bddd6443..906a83b58a6 100644 --- a/app/contexts/issues/list_context.rb +++ b/app/contexts/issues/list_context.rb @@ -8,7 +8,7 @@ module Issues @issues = case params[:status] when issues_filter[:all] then @project.issues when issues_filter[:closed] then @project.issues.closed - when issues_filter[:to_me] then @project.issues.assigned(current_user) + when issues_filter[:to_me] then @project.issues.assigned_to(current_user) when issues_filter[:by_me] then @project.issues.authored(current_user) else @project.issues.opened end diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb index 2922564ba20..d3b8dee3948 100644 --- a/app/contexts/projects/create_context.rb +++ b/app/contexts/projects/create_context.rb @@ -51,6 +51,7 @@ module Projects if shell.import_repository(@project.path_with_namespace, @project.import_url) # We should create satellite for imported repo @project.satellite.create unless @project.satellite.exists? + @project.imported = true true else @project.errors.add(:import_url, 'cannot clone repo') diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index c38461c89db..749c8fbecef 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -8,10 +8,6 @@ class Admin::GroupsController < Admin::ApplicationController end def show - @projects = Project.scoped - @projects = @projects.not_in_group(@group) if @group.projects.present? - @projects = @projects.all - @projects.reject!(&:empty_repo?) end def new diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 185ad181b2a..ec3209fdfe2 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController def create admin = params[:user].delete("admin") - @admin_user = User.new(params[:user], as: :admin) + opts = { + force_random_password: true, + password_expires_at: Time.now + } + + @admin_user = User.new(params[:user].merge(opts), as: :admin) @admin_user.admin = (admin && admin.to_i > 0) + @admin_user.created_by_id = current_user.id respond_to do |format| if @admin_user.save diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09af5b94164..fda05feefc0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,7 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! before_filter :reject_blocked! + before_filter :check_password_expiration before_filter :set_current_user_for_thread before_filter :add_abilities before_filter :dev_tools if Rails.env == 'development' @@ -156,4 +157,10 @@ class ApplicationController < ActionController::Base gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url gon.relative_url_root = Gitlab.config.gitlab.relative_url_root end + + def check_password_expiration + if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now + redirect_to new_profile_password_path and return + end + end end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 00000000000..0e5b42178a7 --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,38 @@ +class PasswordsController < ApplicationController + layout 'navless' + + skip_before_filter :check_password_expiration + + before_filter :set_user + before_filter :set_title + + def new + end + + def create + new_password = params[:user][:password] + new_password_confirmation = params[:user][:password_confirmation] + + result = @user.update_attributes( + password: new_password, + password_confirmation: new_password_confirmation + ) + + if result + @user.update_attributes(password_expires_at: nil) + redirect_to root_path, notice: 'Password successfully changed' + else + render :new + end + end + + private + + def set_user + @user = current_user + end + + def set_title + @title = "New password" + end +end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 49b740af046..b91f68aab5e 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -7,8 +7,12 @@ class SnippetsController < ApplicationController # Allow destroy snippet before_filter :authorize_admin_snippet!, only: [:destroy] + before_filter :set_title + respond_to :html + layout 'navless' + def index @snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20) end @@ -98,4 +102,8 @@ class SnippetsController < ApplicationController def authorize_admin_snippet! return render_404 unless can?(current_user, :admin_personal_snippet, @snippet) end + + def set_title + @title = 'Snippets' + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 663a414fff2..a73d574f22e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -142,7 +142,7 @@ module ApplicationHelper end def user_color_scheme_class - COLOR_SCHEMES[current_user.try(:color_scheme_id)] + COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) end # Define whenever show last push event diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9b142714980..878b0913769 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -48,4 +48,36 @@ module ProjectsHelper def remove_project_message(project) "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" end + + def project_nav_tabs + @nav_tabs ||= get_project_nav_tabs(@project, current_user) + end + + def project_nav_tab?(name) + project_nav_tabs.include? name + end + + private + + def get_project_nav_tabs(project, current_user) + nav_tabs = [:home] + + if project.repo_exists? && can?(current_user, :download_code, project) + nav_tabs << [:files, :commits, :network, :graphs] + end + + if project.repo_exists? && project.merge_requests_enabled + nav_tabs << :merge_requests + end + + if can?(current_user, :admin_project, project) + nav_tabs << :settings + end + + [:issues, :wiki, :wall, :snippets].each do |feature| + nav_tabs << feature if project.send :"#{feature}_enabled" + end + + nav_tabs.flatten + end end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 85337583640..11e3d8eed19 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -22,8 +22,10 @@ module Issuable scope :closed, -> { with_state(:closed) } scope :of_group, ->(group) { where(project_id: group.project_ids) } scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } - scope :assigned, ->(u) { where(assignee_id: u.id)} + scope :assigned_to, ->(u) { where(assignee_id: u.id)} scope :recent, -> { order("created_at DESC") } + scope :assigned, -> { where("assignee_id IS NOT NULL") } + scope :unassigned, -> { where("assignee_id IS NULL") } delegate :name, :email, diff --git a/app/models/issue.rb b/app/models/issue.rb index 91dd6477b04..de6e015c68e 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -27,7 +27,7 @@ class Issue < ActiveRecord::Base scope :cared, ->(user) { where(assignee_id: user) } scope :authored, ->(user) { where(author_id: user) } - scope :open_for, ->(user) { opened.assigned(user) } + scope :open_for, ->(user) { opened.assigned_to(user) } state_machine :state, initial: :opened do event :close do diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b2ad1b76f1f..f41473336bc 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -91,6 +91,15 @@ class MergeRequest < ActiveRecord::Base if target_branch == source_branch errors.add :branch_conflict, "You can not use same branch for source and target branches" end + + if opened? || reopened? + similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened + similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id + + if similar_mrs.any? + errors.add :base, "There is already an open merge request for this branches" + end + end end def reload_code diff --git a/app/models/namespace.rb b/app/models/namespace.rb index cb7164eab13..c74e0cf5a1d 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -27,6 +27,7 @@ class Namespace < ActiveRecord::Base message: "only letters, digits, spaces & '_' '-' '.' allowed." } validates :description, length: { within: 0..255 } validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, + exclusion: { in: Gitlab::Blacklist.path }, format: { with: Gitlab::Regex.path_regex, message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } diff --git a/app/models/project.rb b/app/models/project.rb index f5c2b4fe9af..234d5e98b4f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -79,6 +79,7 @@ class Project < ActiveRecord::Base format: { with: Gitlab::Regex.project_name_regex, message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" } validates :path, presence: true, length: { within: 0..255 }, + exclusion: { in: Gitlab::Blacklist.path }, format: { with: Gitlab::Regex.path_regex, message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } validates :issues_enabled, :wall_enabled, :merge_requests_enabled, @@ -92,7 +93,7 @@ class Project < ActiveRecord::Base format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }, if: :import? - validate :check_limit, :repo_name + validate :check_limit # Scopes scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } @@ -166,14 +167,6 @@ class Project < ActiveRecord::Base errors[:base] << ("Can't check your ability to create project") end - def repo_name - denied_paths = %w(admin dashboard groups help profile projects search) - - if denied_paths.include?(path) - errors.add(:path, "like #{path} is not allowed") - end - end - def to_param if namespace namespace.path + "/" + path @@ -420,6 +413,10 @@ class Project < ActiveRecord::Base !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) end + def imported? + imported + end + def rename_repo old_path_with_namespace = File.join(namespace_dir, path_was) new_path_with_namespace = File.join(namespace_dir, path) diff --git a/app/models/user.rb b/app/models/user.rb index 3f51d7a9938..6de8d2d4c39 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -42,8 +42,11 @@ class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, - :extern_uid, :provider, as: [:default, :admin] - attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin + :extern_uid, :provider, :password_expires_at, + as: [:default, :admin] + + attr_accessible :projects_limit, :can_create_team, :can_create_group, + as: :admin attr_accessor :force_random_password @@ -104,6 +107,7 @@ class User < ActiveRecord::Base validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} validates :username, presence: true, uniqueness: true, + exclusion: { in: Gitlab::Blacklist.path }, format: { with: Gitlab::Regex.username_regex, message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } @@ -363,4 +367,8 @@ class User < ActiveRecord::Base def accessible_deploy_keys DeployKey.in_projects(self.master_projects).uniq end + + def created_by + User.find_by_id(created_by_id) if created_by_id + end end diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index bd88bb838ef..3d4d161a1a2 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,13 +1,13 @@ class ProjectObserver < BaseObserver def after_create(project) - unless project.forked? - GitlabShellWorker.perform_async( - :add_repository, - project.path_with_namespace - ) + return true if project.forked? || project.imported? - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") - end + GitlabShellWorker.perform_async( + :add_repository, + project.path_with_namespace + ) + + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end def after_update(project) diff --git a/app/views/admin/teams/members/new.html.haml b/app/views/admin/teams/members/new.html.haml index d3929cb7cdf..96b6d61b974 100644 --- a/app/views/admin/teams/members/new.html.haml +++ b/app/views/admin/teams/members/new.html.haml @@ -1,29 +1,27 @@ %h3.page_title - Team: #{@team.name} - -%fieldset - %legend Members (#{@team.members.count}) - = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do - %table#members_list - %thead - %tr - %th User name - %th Default project access - %th Team access - %th - - @team.members.each do |member| - %tr.member - %td - = link_to [:admin, member] do - = member.name - %small= "(#{member.email})" - %td= @team.human_default_projects_access(member) - %td= @team.admin?(member) ? "Admin" : "Member" - %td - %tr - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' - %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } - %td - %span= check_box_tag :group_admin - %span Admin? - %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team + New members for + = link_to @team.name, admin_team_path(@team) + team +%hr += form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do + - if @team.errors.any? + .alert.alert-error + %span= @team.errors.full_messages.first + .clearfix + = label_tag :user_ids do + Users to add + .input + = select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' + .clearfix.group-description-holder + = label_tag :default_project_access do + Default permission in projects + .input + = select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } + .clearfix + = label_tag :group_admin do + Is team admin + .input + = check_box_tag :group_admin + .clearfix.form-actions + = submit_tag 'Add users into team', class: "btn btn-primary", id: :add_members_to_team + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 9bde50f8947..fdf37965091 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -24,19 +24,25 @@ = f.text_field :email, required: true, autocomplete: "off" %span.help-inline * required - %fieldset - %legend Password - .clearfix - = f.label :password - .input= f.password_field :password, disabled: f.object.force_random_password - .clearfix - = f.label :password_confirmation - .input= f.password_field :password_confirmation, disabled: f.object.force_random_password - -if f.object.new_record? + - if @admin_user.new_record? + %fieldset + %legend Password .clearfix - = f.label :force_random_password do - %span Generate random password - .input= f.check_box :force_random_password, {}, true, nil + = f.label :password + .input + %strong + A temporary password will be generated and sent to user. + %br + User will be forced to change it after first sign in + - else + %fieldset + %legend Password + .clearfix + = f.label :password + .input= f.password_field :password, disabled: f.object.force_random_password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation, disabled: f.object.force_random_password %fieldset %legend Access diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 6709b8f8a6b..6cf7152e5f7 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -1,32 +1,68 @@ +%h3.page_title + User: + = @admin_user.name + - if @admin_user.blocked? + %span.cred (Blocked) + - if @admin_user.admin + %span.cred (Admin) + + .pull-right + = link_to edit_admin_user_path(@admin_user), class: "btn grouped btn-small" do + %i.icon-edit + Edit + - unless @admin_user == current_user + - if @admin_user.blocked? + = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn grouped btn-small success" + - else + = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove" + = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove" +%hr + .row .span6 - %h3.page_title - = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90" - = @admin_user.name - - if @admin_user.blocked? - %span.cred (Blocked) - - if @admin_user.admin - %span.cred (Admin) - .pull-right - = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do - %i.icon-edit - Edit - %br - %small @#{@admin_user.username} - %br - %small member since #{@admin_user.created_at.stamp("Nov 12, 2031")} - .clearfix - %hr - %p - %span.btn.btn-small - %i.icon-envelope - = mail_to @admin_user.email - - unless @admin_user == current_user - - if @admin_user.blocked? - = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success" - - else - = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" - = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" + .ui-box + %h5.title + Account: + .pull-right + = image_tag gravatar_icon(@admin_user.email, 32), class: "avatar s32" + %ul.well-list + %li + %span.light Name: + %strong= @admin_user.name + %li + %span.light Username: + %strong + = @admin_user.username + %li + %span.light Email: + %strong + = mail_to @admin_user.email + + %li + %span.light Member since: + %strong + = @admin_user.created_at.stamp("Nov 12, 2031") + + %li + %span.light Last sign-in at: + %strong + - if @admin_user.last_sign_in_at + = @admin_user.last_sign_in_at.stamp("Nov 12, 2031") + - else + never + + - if @admin_user.ldap_user? + %li + %span.light LDAP uid: + %strong + = @admin_user.extern_uid + + - if @admin_user.created_by + %li + %span.light Created by: + %strong + = link_to @admin_user.created_by.name, [:admin, @admin_user.created_by] + %hr %h5 Add User to Projects @@ -67,11 +103,11 @@ .span6 - = render 'users/profile', user: @admin_user .ui-box %h5.title Projects (#{@projects.count}) %ul.well-list - @projects.sort_by(&:name_with_namespace).each do |project| + - tm = project.team.get_tm(@admin_user.id) %li = link_to admin_project_path(project), class: dom_class(project) do - if project.namespace @@ -79,16 +115,17 @@ \/ %strong.well-title = truncate(project.name, length: 45) - %span.pull-right.light - - if project.owner == @admin_user - %i.icon-wrench - - tm = project.team.get_tm(@admin_user.id) - - if tm - = tm.project_access_human - = link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do + + - if project.owner == @admin_user + %span.label.label-info owner + + - if tm + .pull-right + = link_to edit_admin_project_member_path(project, tm.user), class: "btn grouped btn-small" do %i.icon-edit - = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn btn-small btn-remove" do + = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn grouped btn-small btn-remove" do %i.icon-remove - %p.light - %i.icon-wrench - – user is a project owner + + .pull-right.light + = tm.project_access_human +   diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml index 710a5d52514..935bc6af505 100644 --- a/app/views/devise/sessions/_oauth_providers.html.haml +++ b/app/views/devise/sessions/_oauth_providers.html.haml @@ -1,8 +1,9 @@ -- if enabled_oauth_providers.present? +- providers = (enabled_oauth_providers - [:ldap]) +- if providers.present? %hr %div{:'data-no-turbolink' => 'data-no-turbolink'} %span Sign in with:   - - (enabled_oauth_providers - [:ldap]).each do |provider| + - providers.each do |provider| %span - if default_providers.include?(provider) = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) diff --git a/app/views/edit_tree/show.html.haml b/app/views/edit_tree/show.html.haml index 17d813ce75e..101b479afed 100644 --- a/app/views/edit_tree/show.html.haml +++ b/app/views/edit_tree/show.html.haml @@ -23,7 +23,7 @@ = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: :file_content .commit-button-annotation - = button_tag "Commit", class: 'btn commit-btn js-commit-button' + = button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-primary' .message to branch %strong= @ref diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 1c5781daab0..c01523c59cd 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -3,43 +3,48 @@ = link_to project_path(@project), title: "Project" do %i.icon-home - - unless @project.empty_repo? - - if can? current_user, :download_code, @project - = nav_link(controller: %w(tree blob blame)) do - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(commit commits compare repositories protected_branches)) do - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(network)) do - = link_to "Network", project_network_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(graphs)) do - = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref) + - if project_nav_tab? :files + = nav_link(controller: %w(tree blob blame)) do + = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) - - if @project.issues_enabled + - if project_nav_tab? :commits + = nav_link(controller: %w(commit commits compare repositories protected_branches)) do + = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) + + - if project_nav_tab? :network + = nav_link(controller: %w(network)) do + = link_to "Network", project_network_path(@project, @ref || @repository.root_ref) + + - if project_nav_tab? :graphs + = nav_link(controller: %w(graphs)) do + = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref) + + - if project_nav_tab? :issues = nav_link(controller: %w(issues milestones labels)) do = link_to url_for_project_issues do Issues - if @project.used_default_issues_tracker? %span.count.issue_counter= @project.issues.opened.count - - if @project.repo_exists? && @project.merge_requests_enabled + - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do = link_to project_merge_requests_path(@project) do Merge Requests %span.count.merge_counter= @project.merge_requests.opened.count - - if @project.wiki_enabled + - if project_nav_tab? :wiki = nav_link(controller: :wikis) do = link_to 'Wiki', project_wiki_path(@project, :home) - - if @project.wall_enabled + - if project_nav_tab? :wall = nav_link(controller: :walls) do = link_to 'Wall', project_wall_path(@project) - - if @project.snippets_enabled + - if project_nav_tab? :snippets = nav_link(controller: :snippets) do = link_to 'Snippets', project_snippets_path(@project) - - if can? current_user, :admin_project, @project + - if project_nav_tab? :settings = nav_link(html_options: {class: "#{project_tab_class}"}) do = link_to edit_project_path(@project), class: "stat-tab tab " do Settings diff --git a/app/views/layouts/nav/_team.html.haml b/app/views/layouts/nav/_team.html.haml index 415e45104df..575c5b7e1f3 100644 --- a/app/views/layouts/nav/_team.html.haml +++ b/app/views/layouts/nav/_team.html.haml @@ -18,7 +18,7 @@ Members %span.count= @team.members.count - - if can? current_user, :admin_user_team, @team + - if can? current_user, :manage_user_team, @team = nav_link(path: 'teams#edit') do = link_to edit_team_path(@team), class: "stat-tab tab " do Settings diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml deleted file mode 100644 index e98aba445af..00000000000 --- a/app/views/layouts/snippets.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render "layouts/head", title: "Snipepts" - %body{class: "#{app_theme} application", :'data-page' => body_data_page} - = render "layouts/head_panel", title: "Snippets" - = render "layouts/flash" - %nav.main-nav - .container - %ul - = nav_link(path: 'snippets#user_index', html_options: {class: 'home'}) do - = link_to user_snippets_path(current_user), title: "My Snippets" do - %i.icon-home - = nav_link(path: 'snippets#new') do - = link_to new_snippet_path do - New snippet - = nav_link(path: 'snippets#index') do - = link_to snippets_path do - Discover snippets - .container - .content= yield diff --git a/app/views/milestones/_issues.html.haml b/app/views/milestones/_issues.html.haml new file mode 100644 index 00000000000..eccf3ddbfa5 --- /dev/null +++ b/app/views/milestones/_issues.html.haml @@ -0,0 +1,11 @@ +.ui-box + %h5.title= title + %ul.well-list + - issues.each do |issue| + %li + = link_to [@project, issue] do + %span.badge{class: issue.closed? ? 'badge-important' : 'badge-info'} ##{issue.id} + = link_to_gfm truncate(issue.title, length: 60), [@project, issue] + - if issue.assignee + .pull-right + = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16" diff --git a/app/views/milestones/_merge_request.html.haml b/app/views/milestones/_merge_request.html.haml new file mode 100644 index 00000000000..7f815894069 --- /dev/null +++ b/app/views/milestones/_merge_request.html.haml @@ -0,0 +1,5 @@ +%li + = link_to [@project, merge_request] do + %span.badge.badge-info ##{merge_request.id} + – + = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request] diff --git a/app/views/milestones/show.html.haml b/app/views/milestones/show.html.haml index 034c37852f1..de33ab363e6 100644 --- a/app/views/milestones/show.html.haml +++ b/app/views/milestones/show.html.haml @@ -55,39 +55,52 @@ = markdown @milestone.description -.row - .span6 - .ui-box.milestone-issue-filter - .title - %ul.nav.nav-pills - %li.active= link_to('Open Issues', '#') - %li=link_to('All Issues', '#') - %ul.well-list - - @issues.each do |issue| - %li{data: {closed: issue.closed?}} - = link_to [@project, issue] do - %span.badge.badge-info ##{issue.id} - – - = link_to_gfm truncate(issue.title, length: 60), [@project, issue] +%ul.nav.nav-tabs + %li.active + = link_to '#tab-issues', 'data-toggle' => 'tab' do + Issues + %span.badge= @issues.count + %li + = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do + Merge Requests + %span.badge= @merge_requests.count + %li + = link_to '#tab-participants', 'data-toggle' => 'tab' do + Participants + %span.badge= @users.count - .span6 - .ui-box.milestone-merge-requests-filter - .title - %ul.nav.nav-pills - %li.active= link_to('Open Merge Requests', '#') - %li=link_to('All Merge Requests', '#') - %ul.well-list - - @merge_requests.each do |merge_request| - %li{data: {closed: merge_request.closed? || merge_request.merged?}} - = link_to [@project, merge_request] do - %span.badge.badge-info ##{merge_request.id} - – - = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request] -%hr -%h6 Participants: -%div - - @users.each do |user| - = link_to_member(@project, user) +.tab-content + .tab-pane.active#tab-issues + .row + .span4 + = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned) + .span4 + = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned) + .span4 + = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed) -.clearfix + .tab-pane#tab-merge-requests + .row + .span6 + .ui-box + %h5.title Open + %ul.well-list + - @merge_requests.opened.each do |merge_request| + = render 'merge_request', merge_request: merge_request + .span6 + .ui-box + %h5.title Closed + %ul.well-list + - @merge_requests.closed.each do |merge_request| + = render 'merge_request', merge_request: merge_request + + .tab-pane#tab-participants + %ul.bordered-list + - @users.each do |user| + %li + = link_to user, title: user.name, class: "dark" do + = image_tag gravatar_icon(user.email, 32), class: "avatar s32" + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.username diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml index 9804fbdd51e..fc2c02ef827 100644 --- a/app/views/notify/new_user_email.html.haml +++ b/app/views/notify/new_user_email.html.haml @@ -8,13 +8,14 @@ %p login.......................................... %code= @user['email'] -%p - - unless Gitlab.config.gitlab.signup_enabled + +- if @user.created_by_id + %p password.................................. %code= @password -%p - Please change your password immediately after login. + %p + You will be forced to change this password immediately after login. %p = link_to "Click here to login", root_url diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb index 777930a2803..70fe0e0736a 100644 --- a/app/views/notify/new_user_email.text.erb +++ b/app/views/notify/new_user_email.text.erb @@ -3,10 +3,11 @@ Hi <%= @user.name %>! The Administrator created an account for you. Now you are a member of company GitLab application. login.................. <%= @user.email %> -<% unless Gitlab.config.gitlab.signup_enabled %> +<% if @user.created_by_id %> password............... <%= @password %> + + You will be forced to change this password immediately after login. <% end %> -Please change your password immediately after login. Click here to login: <%= url_for(root_url) %> diff --git a/app/views/passwords/new.html.haml b/app/views/passwords/new.html.haml new file mode 100644 index 00000000000..c92424160b3 --- /dev/null +++ b/app/views/passwords/new.html.haml @@ -0,0 +1,22 @@ += form_for @user, url: profile_password_path, method: :post do |f| + .light-well.padded + %p.slead + Please set new password before proceed. + %br + After successful password update you will be redirected to login screen + -if @user.errors.any? + .alert.alert-error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password, required: true + .clearfix + = f.label :password_confirmation + .input + = f.password_field :password_confirmation, required: true + .clearfix + .input + = f.submit 'Set new password', class: "btn btn-create" diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml index acaa03f9ad2..346bbd2daf3 100644 --- a/app/views/projects/_settings_nav.html.haml +++ b/app/views/projects/_settings_nav.html.haml @@ -5,8 +5,8 @@ Edit = nav_link(controller: [:team_members, :teams]) do = link_to project_team_index_path(@project), class: "team-tab tab" do - %i.icon-user - Team + %i.icon-group + Project Members = nav_link(controller: :deploy_keys) do = link_to project_deploy_keys_path(@project) do %span @@ -14,7 +14,7 @@ = nav_link(controller: :hooks) do = link_to project_hooks_path(@project) do %span - Hooks + Web Hooks = nav_link(controller: :services) do = link_to project_services_path(@project) do %span diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml index 912f4c77a4b..cf5c3084dc4 100644 --- a/app/views/snippets/current_user_index.html.haml +++ b/app/views/snippets/current_user_index.html.haml @@ -1,8 +1,11 @@ %h3.page_title My Snippets %small share code pastes with others out of git repository - = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do - Add new snippet + .pull-right + = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do + Add new snippet + = link_to snippets_path, class: "btn btn-small grouped" do + Discover snippets %hr diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index 97f7b39877e..4301f90f9d6 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -1,8 +1,12 @@ %h3.page_title Public snippets %small share code pastes with others out of git repository - = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do - Add new snippet + + .pull-right + = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do + Add new snippet + = link_to user_snippets_path(current_user), class: "btn btn-small grouped" do + My snippets %hr .row diff --git a/app/views/team_members/index.html.haml b/app/views/team_members/index.html.haml index 3132fd5ca8a..c0f7ee4330d 100644 --- a/app/views/team_members/index.html.haml +++ b/app/views/team_members/index.html.haml @@ -1,6 +1,6 @@ = render "projects/settings_nav" %h3.page_title - Team Members + Project Members (#{@project.users.count}) %small Read more about project permissions diff --git a/app/views/teams/edit.html.haml b/app/views/teams/edit.html.haml index c9d573ea7e4..7f2273ee26c 100644 --- a/app/views/teams/edit.html.haml +++ b/app/views/teams/edit.html.haml @@ -5,8 +5,9 @@ = link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab' %li = link_to 'Edit Team', '#tab-edit', 'data-toggle' => 'tab' - %li - = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' + - if can? current_user, :admin_user_team, @team + %li + = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' .span9 .tab-content diff --git a/config/environments/production.rb b/config/environments/production.rb index 183b7ae5b70..7e02c75e562 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -52,7 +52,7 @@ Gitlab::Application.configure do # config.action_mailer.raise_delivery_errors = false # Enable threaded mode - config.threadsafe! + config.threadsafe! unless $rails_rake_task # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation can not be found) diff --git a/config/routes.rb b/config/routes.rb index 6f72e2cb186..39c79635c40 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do end resource :notifications + resource :password end resources :keys diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index f119694d11d..632f6107b33 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -3,7 +3,8 @@ admin = User.create( name: "Administrator", username: 'root', password: "5iveL!fe", - password_confirmation: "5iveL!fe" + password_confirmation: "5iveL!fe", + password_expires_at: Time.now ) admin.projects_limit = 10000 diff --git a/db/migrate/20130613165816_add_password_expires_at_to_users.rb b/db/migrate/20130613165816_add_password_expires_at_to_users.rb new file mode 100644 index 00000000000..3479c8e64d0 --- /dev/null +++ b/db/migrate/20130613165816_add_password_expires_at_to_users.rb @@ -0,0 +1,5 @@ +class AddPasswordExpiresAtToUsers < ActiveRecord::Migration + def change + add_column :users, :password_expires_at, :datetime + end +end diff --git a/db/migrate/20130613173246_add_created_by_id_to_user.rb b/db/migrate/20130613173246_add_created_by_id_to_user.rb new file mode 100644 index 00000000000..615e96eb156 --- /dev/null +++ b/db/migrate/20130613173246_add_created_by_id_to_user.rb @@ -0,0 +1,5 @@ +class AddCreatedByIdToUser < ActiveRecord::Migration + def change + add_column :users, :created_by_id, :integer + end +end diff --git a/db/migrate/20130614132337_add_improted_to_project.rb b/db/migrate/20130614132337_add_improted_to_project.rb new file mode 100644 index 00000000000..cc882c3f10a --- /dev/null +++ b/db/migrate/20130614132337_add_improted_to_project.rb @@ -0,0 +1,5 @@ +class AddImprotedToProject < ActiveRecord::Migration + def change + add_column :projects, :imported, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 21e553dd612..c3c751e3a19 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130522141856) do +ActiveRecord::Schema.define(:version => 20130614132337) do create_table "deploy_keys_projects", :force => true do |t| t.integer "deploy_key_id", :null => false @@ -172,6 +172,7 @@ ActiveRecord::Schema.define(:version => 20130522141856) do t.string "issues_tracker_id" t.boolean "snippets_enabled", :default => true, :null => false t.datetime "last_activity_at" + t.boolean "imported", :default => false, :null => false end add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" @@ -292,6 +293,8 @@ ActiveRecord::Schema.define(:version => 20130522141856) do t.string "state" t.integer "color_scheme_id", :default => 1, :null => false t.integer "notification_level", :default => 1, :null => false + t.datetime "password_expires_at" + t.integer "created_by_id" end add_index "users", ["admin"], :name => "index_users_on_admin" diff --git a/doc/api/README.md b/doc/api/README.md index 9120fe3aea4..6faf6dcc7c2 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -69,15 +69,15 @@ When listing resources you can pass the following parameters: ## Contents -+ [Users](doc/api/users.md) -+ [Session](doc/api/session.md) -+ [Projects](doc/api/projects.md) -+ [Project Snippets](doc/api/project_snippets.md) -+ [Repositories](doc/api/repositories.md) -+ [Issues](doc/api/issues.md) -+ [Milestones](doc/api/milestones.md) -+ [Notes](doc/api/notes.md) -+ [Deploy Keys](doc/api/deploy_keys.md) -+ [System Hooks](doc/api/system_hooks.md) -+ [Groups](doc/api/groups.md) -+ [User Teams](doc/api/user_teams.md) ++ [Users](users.md) ++ [Session](session.md) ++ [Projects](projects.md) ++ [Project Snippets](project_snippets.md) ++ [Repositories](repositories.md) ++ [Issues](issues.md) ++ [Milestones](milestones.md) ++ [Notes](notes.md) ++ [Deploy Keys](deploy_keys.md) ++ [System Hooks](system_hooks.md) ++ [Groups](groups.md) ++ [User Teams](user_teams.md) diff --git a/doc/install/installation.md b/doc/install/installation.md index c94bfe1b76b..a16c5acf1b1 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -148,10 +148,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install cd /home/git/gitlab # Checkout to stable release - sudo -u git -H git checkout 5-2-stable + sudo -u git -H git checkout 5-3-stable **Note:** -You can change `5-2-stable` to `master` if you want the *bleeding edge* version, but do so with caution! +You can change `5-3-stable` to `master` if you want the *bleeding edge* version, but do so with caution! ## Configure it @@ -352,10 +352,10 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. -* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/Gemfile#L18) +* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/Gemfile#L18) * Run `sudo -u git -H bundle install` to install the new gem(s) -* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example#L53) as a reference) -* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-2-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) +* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/gitlab.yml.example#L53) as a reference) +* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-3-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) * Restart GitLab ### Examples diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md new file mode 100644 index 00000000000..ff95e1c05c4 --- /dev/null +++ b/doc/update/5.2-to-5.3.md @@ -0,0 +1,80 @@ +# From 5.2 to 5.3 + +### 0. 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 RAILS_ENV=production bundle exec rake gitlab:backup:create +``` + +### 1. Stop server + + sudo service gitlab stop + +### 2. Get latest code + +```bash +cd /home/git/gitlab +sudo -u git -H git fetch +sudo -u git -H git checkout 5-3-stable +``` + +### 3. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL +sudo -u git -H bundle install --without development test postgres --deployment + +#PostgreSQL +sudo -u git -H bundle install --without development test mysql --deployment + +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production +``` + +### 4. Update config files + +* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example but with your settings. +* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/puma.rb.example but with your settings. + +### 5. Update Init script + +```bash +sudo rm /etc/init.d/gitlab +sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/5-3-stable/lib/support/init.d/gitlab +sudo chmod +x /etc/init.d/gitlab +``` + +### 6. Start application + + sudo service gitlab start + sudo service nginx restart + +### 7. Check application status + +Check if GitLab and its environment are configured correctly: + + 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! + +## Things went south? Revert to previous version (5.2) + +### 1. Revert the code to the previous version +Follow the [`upgrade guide from 5.1 to 5.2`](5.1-to-5.2.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 RAILS_ENV=production bundle exec rake gitlab:backup:restore +``` diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature index 50c090cc6a0..2f38acf14d0 100644 --- a/features/project/issues/milestones.feature +++ b/features/project/issues/milestones.feature @@ -22,5 +22,3 @@ Feature: Project Milestones Given the milestone has open and closed issues And I click link "v2.2" Then I should see 3 issues - When I click link "All Issues" - Then I should see 4 issues diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/project_active_tab.rb index 4c6d890fe4b..5170ab82e8a 100644 --- a/features/steps/project/project_active_tab.rb +++ b/features/steps/project/project_active_tab.rb @@ -45,7 +45,7 @@ class ProjectActiveTab < Spinach::FeatureSteps # Sub Tabs: Home Given 'I click the "Team" tab' do - click_link('Team') + click_link('Project Members') end Given 'I click the "Attachments" tab' do @@ -61,7 +61,7 @@ class ProjectActiveTab < Spinach::FeatureSteps end Given 'I click the "Hooks" tab' do - click_link('Hooks') + click_link('Web Hooks') end Given 'I click the "Deploy Keys" tab' do @@ -73,7 +73,7 @@ class ProjectActiveTab < Spinach::FeatureSteps end Then 'the active sub tab should be Team' do - ensure_active_sub_tab('Team') + ensure_active_sub_tab('Project Members') end Then 'the active sub tab should be Attachments' do @@ -89,7 +89,7 @@ class ProjectActiveTab < Spinach::FeatureSteps end Then 'the active sub tab should be Hooks' do - ensure_active_sub_tab('Hooks') + ensure_active_sub_tab('Web Hooks') end Then 'the active sub tab should be Deploy Keys' do diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/project_merge_requests.rb index 6a2f870e276..ea434412bb2 100644 --- a/features/steps/project/project_merge_requests.rb +++ b/features/steps/project/project_merge_requests.rb @@ -57,8 +57,8 @@ class ProjectMergeRequests < Spinach::FeatureSteps And 'I submit new merge request "Wiki Feature"' do fill_in "merge_request_title", with: "Wiki Feature" - select "master", from: "merge_request_source_branch" - select "stable", from: "merge_request_target_branch" + select "bootstrap", from: "merge_request_source_branch" + select "master", from: "merge_request_target_branch" click_button "Submit merge request" end diff --git a/features/steps/project/project_milestones.rb b/features/steps/project/project_milestones.rb index fcd590fcab2..c4d0d176f3a 100644 --- a/features/steps/project/project_milestones.rb +++ b/features/steps/project/project_milestones.rb @@ -50,12 +50,6 @@ class ProjectMilestones < Spinach::FeatureSteps end Then "I should see 3 issues" do - page.should have_selector('.milestone-issue-filter .well-list li', count: 4) - page.should have_selector('.milestone-issue-filter .well-list li.hide', count: 1) - end - - Then "I should see 4 issues" do - page.should have_selector('.milestone-issue-filter .well-list li', count: 4) - page.should_not have_selector('.milestone-issue-filter .well-list li.hide') + page.should have_selector('#tab-issues li', count: 4) end end diff --git a/features/steps/userteams/userteams.rb b/features/steps/userteams/userteams.rb index 9a86572e1ac..b4b2fb66a50 100644 --- a/features/steps/userteams/userteams.rb +++ b/features/steps/userteams/userteams.rb @@ -93,7 +93,7 @@ class Userteams < Spinach::FeatureSteps Then 'I should see issues from this team assigned to me' do team = UserTeam.last team.projects.each do |project| - project.issues.assigned(current_user).each do |issue| + project.issues.assigned_to(current_user).each do |issue| page.should have_content issue.title end end @@ -121,7 +121,7 @@ class Userteams < Spinach::FeatureSteps team = UserTeam.last team.projects.each do |project| team.members.each do |member| - project.issues.assigned(member).each do |issue| + project.issues.assigned_to(member).each do |issue| page.should have_content issue.title end end @@ -131,9 +131,7 @@ class Userteams < Spinach::FeatureSteps Given 'project from team has merge requests assigned to me' do team = UserTeam.last team.projects.each do |project| - team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } - end + create(:merge_request, assignee: current_user, project: project) end end @@ -145,10 +143,8 @@ class Userteams < Spinach::FeatureSteps Then 'I should see merge requests from this team assigned to me' do team = UserTeam.last team.projects.each do |project| - team.members.each do |member| - project.issues.assigned(member).each do |merge_request| - page.should have_content merge_request.title - end + project.merge_requests.each do |merge_request| + page.should have_content merge_request.title end end end @@ -156,20 +152,8 @@ class Userteams < Spinach::FeatureSteps Given 'project from team has merge requests assigned to team members' do team = UserTeam.last team.projects.each do |project| - team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } - end - end - end - - Then 'I should see merge requests from this team assigned to me' do - team = UserTeam.last - team.projects.each do |project| - team.members.each do |member| - project.issues.assigned(member).each do |merge_request| - page.should have_content merge_request.title - end - end + member = team.members.sample + create(:merge_request, assignee: member, project: project) end end diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 4f3f7b02a5b..e7217c7c7e6 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -1,9 +1,13 @@ require_relative 'shell_env' -require 'omniauth-ldap' +require_relative 'grack_ldap' +require_relative 'grack_helpers' module Grack class Auth < Rack::Auth::Basic - attr_accessor :user, :project + include LDAP + include Helpers + + attr_accessor :user, :project, :ref, :env def call(env) @env = env @@ -14,42 +18,52 @@ module Grack @env['PATH_INFO'] = @request.path @env['SCRIPT_NAME'] = "" - return render_not_found unless project - return unauthorized unless project.public || @auth.provided? - return bad_request if @auth.provided? && !@auth.basic? + auth! + end - if valid? - if @auth.provided? + private + + def auth! + return render_not_found unless project + + if @auth.provided? + return bad_request unless @auth.basic? + + # Authentication with username and password + login, password = @auth.credentials + + @user = authenticate_user(login, password) + + if @user + Gitlab::ShellEnv.set_env(@user) @env['REMOTE_USER'] = @auth.username + else + return unauthorized end - return @app.call(env) + + else + return unauthorized unless project.public + end + + if authorized_git_request? + @app.call(env) else unauthorized end end - def valid? - if @auth.provided? - # Authentication with username and password - login, password = @auth.credentials - - @user = authenticate(login, password) - return false unless @user - - Gitlab::ShellEnv.set_env(@user) - end - + def authorized_git_request? # Git upload and receive if @request.get? - validate_get_request + authorize_request(@request.params['service']) elsif @request.post? - validate_post_request + authorize_request(File.basename(@request.path)) else false end end - def authenticate(login, password) + def authenticate_user(login, password) user = User.find_by_email(login) || User.find_by_username(login) # If the provided login was not a known email or username @@ -65,34 +79,12 @@ module Grack end end - def ldap_auth(login, password) - # Check user against LDAP backend if user is not authenticated - # Only check with valid login and password to prevent anonymous bind results - return nil unless ldap_conf.enabled && !login.blank? && !password.blank? - - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) - ldap_user = ldap.bind_as( - filter: Net::LDAP::Filter.eq(ldap.uid, login), - size: 1, - password: password - ) - - User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user - end - - def validate_get_request - validate_request(@request.params['service']) - end - - def validate_post_request - validate_request(File.basename(@request.path)) - end - - def validate_request(service) - if service == 'git-upload-pack' + def authorize_request(service) + case service + when 'git-upload-pack' project.public || can?(user, :download_code, project) - elsif service == 'git-receive-pack' - action = if project.protected_branch?(current_ref) + when'git-receive-pack' + action = if project.protected_branch?(ref) :push_code_to_protected_branches else :push_code @@ -104,49 +96,24 @@ module Grack end end - def can?(object, action, subject) - abilities.allowed?(object, action, subject) + def project + @project ||= project_by_path(@request.path_info) end - def current_ref - if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ - input = Zlib::GzipReader.new(@request.body).read - else - input = @request.body.read - end + def ref + @ref ||= parse_ref + end + + def parse_ref + input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ + Zlib::GzipReader.new(@request.body).read + else + @request.body.read + end + # Need to reset seek point @request.body.rewind /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last end - - def project - unless instance_variable_defined? :@project - # Find project by PATH_INFO from env - if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a - @project = Project.find_with_namespace(m.last) - end - end - return @project - end - - PLAIN_TYPE = {"Content-Type" => "text/plain"} - - def render_not_found - [404, PLAIN_TYPE, ["Not Found"]] - end - - protected - - def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end - end - - def ldap_conf - @ldap_conf ||= Gitlab.config.ldap - end - end# Auth -end# Grack + end +end diff --git a/lib/gitlab/backend/grack_helpers.rb b/lib/gitlab/backend/grack_helpers.rb new file mode 100644 index 00000000000..88b2d167312 --- /dev/null +++ b/lib/gitlab/backend/grack_helpers.rb @@ -0,0 +1,28 @@ +module Grack + module Helpers + def project_by_path(path) + if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a + path_with_namespace = m.last + path_with_namespace.gsub!(/.wiki$/, '') + + Project.find_with_namespace(path_with_namespace) + end + end + + def render_not_found + [404, {"Content-Type" => "text/plain"}, ["Not Found"]] + end + + def can?(object, action, subject) + abilities.allowed?(object, action, subject) + end + + def abilities + @abilities ||= begin + abilities = Six.new + abilities << Ability + abilities + end + end + end +end diff --git a/lib/gitlab/backend/grack_ldap.rb b/lib/gitlab/backend/grack_ldap.rb new file mode 100644 index 00000000000..45e98fbac1e --- /dev/null +++ b/lib/gitlab/backend/grack_ldap.rb @@ -0,0 +1,24 @@ +require 'omniauth-ldap' + +module Grack + module LDAP + def ldap_auth(login, password) + # Check user against LDAP backend if user is not authenticated + # Only check with valid login and password to prevent anonymous bind results + return nil unless ldap_conf.enabled && !login.blank? && !password.blank? + + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) + ldap_user = ldap.bind_as( + filter: Net::LDAP::Filter.eq(ldap.uid, login), + size: 1, + password: password + ) + + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user + end + + def ldap_conf + @ldap_conf ||= Gitlab.config.ldap + end + end +end diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb new file mode 100644 index 00000000000..b678a83fabf --- /dev/null +++ b/lib/gitlab/blacklist.rb @@ -0,0 +1,9 @@ +module Gitlab + module Blacklist + extend self + + def path + %w(admin dashboard groups help profile projects search public assets u s teams merge_requests issues users snippets ) + end + end +end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index bec43e5029c..15de101a17a 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -20,13 +20,10 @@ describe "Admin::Users" do describe "GET /admin/users/new" do before do - @password = "123ABC" visit new_admin_user_path fill_in "user_name", with: "Big Bang" fill_in "user_username", with: "bang" fill_in "user_email", with: "bigbang@mail.com" - fill_in "user_password", with: @password - fill_in "user_password_confirmation", with: @password end it "should create new user" do @@ -57,26 +54,13 @@ describe "Admin::Users" do end it "should send valid email to user with email & password" do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) User.observers.enable :user_observer do click_button "Create user" user = User.last email = ActionMailer::Base.deliveries.last email.subject.should have_content("Account was created") email.text_part.body.should have_content(user.email) - email.text_part.body.should have_content(@password) - end - end - - it "should send valid email to user with email without password when signup is enabled" do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - User.observers.enable :user_observer do - click_button "Create user" - user = User.last - email = ActionMailer::Base.deliveries.last - email.subject.should have_content("Account was created") - email.text_part.body.should have_content(user.email) - email.text_part.body.should_not have_content(@password) + email.text_part.body.should have_content('password') end end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 84ce7e86d27..d2e1e8a8743 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -15,7 +15,7 @@ describe Notify do describe 'for new users, the email' do let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: 'newguy@example.com') } + let(:new_user) { create(:user, email: 'newguy@example.com', created_by_id: 1) } subject { Notify.new_user_email(new_user.id, new_user.password) } @@ -32,8 +32,7 @@ describe Notify do end it 'contains the new user\'s password' do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) - should have_body_text /#{new_user.password}/ + should have_body_text /password/ end it 'includes a link to the site' do @@ -61,8 +60,7 @@ describe Notify do end it 'should not contain the new user\'s password' do - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - should_not have_body_text /#{new_user.password}/ + should_not have_body_text /password/ end it 'includes a link to the site' do