Commit 10528341 authored by Patricio Cano's avatar Patricio Cano

Merge branch 'master' into feature_password_strength_indicator

parents 91c96b37 c6efa56e
...@@ -39,3 +39,4 @@ public/assets/ ...@@ -39,3 +39,4 @@ public/assets/
.envrc .envrc
dump.rdb dump.rdb
tags tags
.gitlab_shell_secret
v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert)
- Add time zone configuration on gitlab.yml (Sullivan Senechal)
v 7.4.0 v 7.4.0
- Refactored membership logic - Refactored membership logic
- Improve error reporting on users API (Julien Bianchi) - Improve error reporting on users API (Julien Bianchi)
...@@ -20,6 +24,21 @@ v 7.4.0 ...@@ -20,6 +24,21 @@ v 7.4.0
- Add select field type for services options (Sullivan Senechal) - Add select field type for services options (Sullivan Senechal)
- Add cross-project references to the Markdown parser (Vinnie Okada) - Add cross-project references to the Markdown parser (Vinnie Okada)
- Add task lists to issue and merge request descriptions (Vinnie Okada) - Add task lists to issue and merge request descriptions (Vinnie Okada)
- Snippets can be public, internal or private
- Improve danger zone: ask project path to confirm data-loss action
- Raise exception on forgery
- Show build coverage in Merge Requests (requires GitLab CI v5.1)
- New milestone and label links on issue edit form
- Improved repository graphs
- Improve event note display in dashboard and project activity views (Vinnie Okada)
- Add users sorting to admin area
- UI improvements
- Fix ambiguous sha problem with mentioned commit
- Fixed bug with apostrophe when at mentioning users
- Add active directory ldap option
- Developers can push to wiki repo. Protected branches does not affect wiki repo any more
- Faster rev list
- Fix branch removal
v 7.3.2 v 7.3.2
- Fix creating new file via web editor - Fix creating new file via web editor
......
...@@ -92,6 +92,7 @@ For examples of feedback on merge requests please look at already [closed merge ...@@ -92,6 +92,7 @@ For examples of feedback on merge requests please look at already [closed merge
1. The change is as small as possible (see the above paragraph for details) 1. The change is as small as possible (see the above paragraph for details)
1. Include proper tests and make all tests pass (unless it contains a test exposing a bug in existing code) 1. Include proper tests and make all tests pass (unless it contains a test exposing a bug in existing code)
1. All tests have to pass, if you suspect a failing CI build is unrelated to your contribution ask for tests to be restarted. See [the CI setup document](http://doc.gitlab.com/ce/development/ci_setup.html) on who you can ask for test restart.
1. Initially contains a single commit (please use `git rebase -i` to squash commits) 1. Initially contains a single commit (please use `git rebase -i` to squash commits)
1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server) 1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server)
1. Does not break any existing functionality 1. Does not break any existing functionality
...@@ -100,7 +101,11 @@ For examples of feedback on merge requests please look at already [closed merge ...@@ -100,7 +101,11 @@ For examples of feedback on merge requests please look at already [closed merge
1. Contains functionality we think other users will benefit from too 1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes 1. Doesn't add configuration options since they complicate future changes
1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging. 1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging.
1. It conforms to the following style guides 1. It conforms to the following style guides.
If your change touches a line that does not follow the style,
modify the entire line to follow it. This prevents linting tools from generating warnings.
Don't touch neighbouring lines. As an exception, automatic mass refactoring modifications
may leave style non-compliant.
## Style guides ## Style guides
......
...@@ -168,7 +168,7 @@ GEM ...@@ -168,7 +168,7 @@ GEM
multi_json multi_json
gitlab-grack (2.0.0.pre) gitlab-grack (2.0.0.pre)
rack (~> 1.5.1) rack (~> 1.5.1)
gitlab-grit (2.6.11) gitlab-grit (2.6.12)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -241,7 +241,8 @@ GEM ...@@ -241,7 +241,8 @@ GEM
html-pipeline (1.11.0) html-pipeline (1.11.0)
activesupport (>= 2) activesupport (>= 2)
nokogiri (~> 1.4) nokogiri (~> 1.4)
html-pipeline-gitlab (0.1.3) html-pipeline-gitlab (0.1.5)
actionpack (~> 4)
gitlab_emoji (~> 0.0.1) gitlab_emoji (~> 0.0.1)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
sanitize (~> 2.1) sanitize (~> 2.1)
......
...@@ -51,7 +51,7 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ...@@ -51,7 +51,7 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a
## Installation ## Installation
Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options. Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options.
Since a manual installation is a lot of work and error prone we strongly recommend fast and reliable Omnibus package installation (deb/rpm) on that page. Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm).
## Third-party applications ## Third-party applications
......
7.4.0-pre 7.5.0.pre
class Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true
$(".event_filter_link").bind "click", (event) => $(".event_filter_link").bind "click", (event) =>
...@@ -27,5 +27,3 @@ class Activities ...@@ -27,5 +27,3 @@ class Activities
event_filters.splice index, 1 event_filters.splice index, 1
$.cookie "event_filter", event_filters.join(","), { path: '/' } $.cookie "event_filter", event_filters.join(","), { path: '/' }
@Activities = Activities
class Admin class @Admin
constructor: -> constructor: ->
$('input#user_force_random_password').on 'change', (elem) -> $('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation') elems = $('#user_password, #user_password_confirmation')
...@@ -51,5 +51,3 @@ class Admin ...@@ -51,5 +51,3 @@ class Admin
$('li.group_member').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
@Admin = Admin
class BlobView class @BlobView
constructor: -> constructor: ->
# handle multi-line select # handle multi-line select
handleMultiSelect = (e) -> handleMultiSelect = (e) ->
...@@ -71,6 +71,3 @@ class BlobView ...@@ -71,6 +71,3 @@ class BlobView
# Highlight the correct lines when the hash part of the URL changes # Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines) $(window).on("hashchange", highlightBlobLines)
@BlobView = BlobView
class Commit class @Commit
constructor: -> constructor: ->
$('.files .diff-file').each -> $('.files .diff-file').each ->
new CommitFile(this) new CommitFile(this)
@Commit = Commit
class CommitFile class @CommitFile
constructor: (file) -> constructor: (file) ->
if $('.image', file).length if $('.image', file).length
new ImageFile(file) new ImageFile(file)
@CommitFile = CommitFile
class ImageFile class @ImageFile
# Width where images must fits in, for 2-up this gets divided by 2 # Width where images must fits in, for 2-up this gets divided by 2
@availWidth = 900 @availWidth = 900
...@@ -124,5 +124,3 @@ class ImageFile ...@@ -124,5 +124,3 @@ class ImageFile
else else
img.on 'load', => img.on 'load', =>
callback.call(this, domImg.naturalWidth, domImg.naturalHeight) callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
@ImageFile = ImageFile
class CommitsList class @CommitsList
@data = @data =
ref: null ref: null
limit: 0 limit: 0
...@@ -53,5 +53,3 @@ class CommitsList ...@@ -53,5 +53,3 @@ class CommitsList
@disable @disable
callback: => callback: =>
this.getOld() this.getOld()
this.CommitsList = CommitsList
class ConfirmDangerModal class @ConfirmDangerModal
constructor: (form, text) -> constructor: (form, text) ->
@form = form @form = form
$('.js-confirm-text').text(text || '') $('.js-confirm-text').text(text || '')
...@@ -16,5 +16,3 @@ class ConfirmDangerModal ...@@ -16,5 +16,3 @@ class ConfirmDangerModal
$('.js-confirm-danger-submit').on 'click', => $('.js-confirm-danger-submit').on 'click', =>
@form.submit() @form.submit()
@ConfirmDangerModal = ConfirmDangerModal
class Dashboard class @Dashboard
constructor: -> constructor: ->
@initSidebarTab() @initSidebarTab()
...@@ -28,6 +28,3 @@ class Dashboard ...@@ -28,6 +28,3 @@ class Dashboard
# show tab from cookie # show tab from cookie
sidebar_filter = $.cookie(key) sidebar_filter = $.cookie(key)
$("#" + sidebar_filter).tab('show') if sidebar_filter $("#" + sidebar_filter).tab('show') if sidebar_filter
@Dashboard = Dashboard
class Diff class @Diff
UNFOLD_COUNT = 20 UNFOLD_COUNT = 20
constructor: -> constructor: ->
$(document).on('click', '.js-unfold', (event) => $(document).on('click', '.js-unfold', (event) =>
...@@ -41,6 +41,3 @@ class Diff ...@@ -41,6 +41,3 @@ class Diff
lines = line.children().slice(0, 2) lines = line.children().slice(0, 2)
line_numbers = ($(l).attr('data-linenumber') for l in lines) line_numbers = ($(l).attr('data-linenumber') for l in lines)
(parseInt(line_number) for line_number in line_numbers) (parseInt(line_number) for line_number in line_numbers)
@Diff = Diff
class Flash class @Flash
constructor: (message, type)-> constructor: (message, type)->
flash = $(".flash-container") flash = $(".flash-container")
flash.html("") flash.html("")
...@@ -10,5 +10,3 @@ class Flash ...@@ -10,5 +10,3 @@ class Flash
flash.click -> $(@).fadeOut() flash.click -> $(@).fadeOut()
flash.show() flash.show()
@Flash = Flash
class GroupMembers class @GroupMembers
constructor: -> constructor: ->
$('li.group_member').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
$(this).fadeOut() $(this).fadeOut()
@GroupMembers = GroupMembers
$ -> $ ->
# avatar # avatar
$('.js-choose-group-avatar-button').bind "click", -> $('.js-choose-group-avatar-button').bind "click", ->
......
class Issue class @Issue
constructor: -> constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide() $('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", -> $(".issue-box .inline-update").on "change", "select", ->
...@@ -15,5 +15,3 @@ class Issue ...@@ -15,5 +15,3 @@ class Issue
"issue" "issue"
updateTaskState updateTaskState
) )
@Issue = Issue
class Labels class @Labels
constructor: -> constructor: ->
form = $('.label-form') form = $('.label-form')
@setupLabelForm(form) @setupLabelForm(form)
...@@ -31,5 +31,3 @@ class Labels ...@@ -31,5 +31,3 @@ class Labels
# Notify the form, that color has changed # Notify the form, that color has changed
$('.label-form').trigger('keyup') $('.label-form').trigger('keyup')
e.preventDefault() e.preventDefault()
@Labels = Labels
class MergeRequest class @MergeRequest
constructor: (@opts) -> constructor: (@opts) ->
@initContextWidget() @initContextWidget()
this.$el = $('.merge-request') this.$el = $('.merge-request')
...@@ -132,5 +132,3 @@ class MergeRequest ...@@ -132,5 +132,3 @@ class MergeRequest
this.$('.automerge_widget').hide() this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide() this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show() this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest
class Milestone class @Milestone
@updateIssue: (li, issue_url, data) -> @updateIssue: (li, issue_url, data) ->
$.ajax $.ajax
type: "PUT" type: "PUT"
...@@ -115,5 +115,3 @@ class Milestone ...@@ -115,5 +115,3 @@ class Milestone
Milestone.updateMergeRequest(ui.item, merge_request_url, data) Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection() ).disableSelection()
@Milestone = Milestone
class Notes class @Notes
@interval: null @interval: null
constructor: (notes_url, note_ids, last_fetched_at) -> constructor: (notes_url, note_ids, last_fetched_at) ->
...@@ -514,7 +514,3 @@ class Notes ...@@ -514,7 +514,3 @@ class Notes
else else
form.find('.js-note-target-reopen').text('Reopen') form.find('.js-note-target-reopen').text('Reopen')
form.find('.js-note-target-close').text('Close') form.find('.js-note-target-close').text('Close')
@Notes = Notes
class NotesVotes class @NotesVotes
updateVotes: -> updateVotes: ->
votes = $("#votes .votes") votes = $("#votes .votes")
notes = $("#notes-list .note .vote") notes = $("#notes-list .note .vote")
...@@ -18,5 +18,3 @@ class NotesVotes ...@@ -18,5 +18,3 @@ class NotesVotes
# replace vote numbers # replace vote numbers
votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes) votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes)
votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes) votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes)
@NotesVotes = NotesVotes
class Project class @Project
constructor: -> constructor: ->
$('.project-edit-container').on 'ajax:before', => $('.project-edit-container').on 'ajax:before', =>
$('.project-edit-container').hide() $('.project-edit-container').hide()
...@@ -24,9 +24,6 @@ class Project ...@@ -24,9 +24,6 @@ class Project
else else
$('#project_issues_tracker_id').removeAttr('disabled') $('#project_issues_tracker_id').removeAttr('disabled')
@Project = Project
$ -> $ ->
# Git clone panel switcher # Git clone panel switcher
scope = $ '.git-clone-holder' scope = $ '.git-clone-holder'
......
class ProjectImport class @ProjectImport
constructor: -> constructor: ->
setTimeout -> setTimeout ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
, 5000 , 5000
@ProjectImport = ProjectImport
class SearchAutocomplete class @SearchAutocomplete
constructor: (search_autocomplete_path, project_id, project_ref) -> constructor: (search_autocomplete_path, project_id, project_ref) ->
project_id = '' unless project_id project_id = '' unless project_id
project_ref = '' unless project_ref project_ref = '' unless project_ref
...@@ -9,5 +9,3 @@ class SearchAutocomplete ...@@ -9,5 +9,3 @@ class SearchAutocomplete
minLength: 1 minLength: 1
select: (event, ui) -> select: (event, ui) ->
location.href = ui.item.url location.href = ui.item.url
@SearchAutocomplete = SearchAutocomplete
class window.StatGraph class @StatGraph
@log: {} @log: {}
@get_log: -> @get_log: ->
@log @log
......
class window.ContributorsStatGraph class @ContributorsStatGraph
init: (log) -> init: (log) ->
@parsed_log = ContributorsStatGraphUtil.parse_log(log) @parsed_log = ContributorsStatGraphUtil.parse_log(log)
@set_current_field("commits") @set_current_field("commits")
......
class window.ContributorsGraph class @ContributorsGraph
MARGIN: MARGIN:
top: 20 top: 20
right: 20 right: 20
...@@ -44,7 +44,7 @@ class window.ContributorsGraph ...@@ -44,7 +44,7 @@ class window.ContributorsGraph
set_data: (data) -> set_data: (data) ->
@data = data @data = data
class window.ContributorsMasterGraph extends ContributorsGraph class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width() - 70 @width = $('.container').width() - 70
@height = 200 @height = 200
...@@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph ...@@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph
@svg.select("path").attr("d", @area) @svg.select("path").attr("d", @area)
@svg.select(".y.axis").call(@y_axis) @svg.select(".y.axis").call(@y_axis)
class window.ContributorsAuthorGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width()/2 - 100 @width = $('.container').width()/2 - 100
@height = 200 @height = 200
......
class TeamMembers class @TeamMembers
constructor: -> constructor: ->
$('.team-members .project-access-select').on "change", -> $('.team-members .project-access-select').on "change", ->
$(this.form).submit() $(this.form).submit()
@TeamMembers = TeamMembers
class TreeView class @TreeView
constructor: -> constructor: ->
@initKeyNav() @initKeyNav()
...@@ -39,5 +39,3 @@ class TreeView ...@@ -39,5 +39,3 @@ class TreeView
else if e.which is 13 else if e.which is 13
path = $('.tree-item.selected .tree-item-file-name a').attr('href') path = $('.tree-item.selected .tree-item-file-name a').attr('href')
Turbolinks.visit(path) Turbolinks.visit(path)
@TreeView = TreeView
class Wikis class @Wikis
constructor: -> constructor: ->
$('.build-new-wiki').bind "click", -> $('.build-new-wiki').bind "click", ->
field = $('#new_wiki_path') field = $('#new_wiki_path')
...@@ -7,6 +7,3 @@ class Wikis ...@@ -7,6 +7,3 @@ class Wikis
if(slug.length > 0) if(slug.length > 0)
location.href = path + "/" + slug location.href = path + "/" + slug
@Wikis = Wikis
/** Typo **/ /** Typo **/
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif; $regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif;
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
} }
.participants { .participants {
margin-bottom: 10px; margin-bottom: 20px;
} }
.issues_bulk_update { .issues_bulk_update {
......
...@@ -111,31 +111,38 @@ ...@@ -111,31 +111,38 @@
.ci_widget { .ci_widget {
padding: 10px 15px; padding: 10px 15px;
font-size: 15px; font-size: 15px;
border-bottom: 1px dashed #AAA; border-bottom: 1px solid #BBB;
color: #777;
background-color: #F5F5F5;
&.ci-success { &.ci-success {
color: $bg_success; color: $bg_success;
border-color: $border_success; border-color: $border_success;
background-color: #F1FAF1;
} }
&.ci-pending { &.ci-pending {
color: #548; color: #548;
border-color: #548; border-color: #548;
background-color: #F4F1FA;
} }
&.ci-running { &.ci-running {
color: $bg_warning; color: $bg_warning;
border-color: $border_warning; border-color: $border_warning;
background-color: #FAF5F1;
} }
&.ci-failed { &.ci-failed {
color: $bg_danger; color: $bg_danger;
border-color: $border_danger; border-color: $border_danger;
background-color: #FAF1F1;
} }
&.ci-error { &.ci-error {
color: $bg_danger; color: $bg_danger;
border-color: $border_danger; border-color: $border_danger;
background-color: #FAF1F1;
} }
} }
...@@ -143,7 +150,8 @@ ...@@ -143,7 +150,8 @@
padding: 10px 15px; padding: 10px 15px;
h4 { h4 {
margin-top: 0px; font-size: 20px;
font-weight: normal;
} }
p:last-child { p:last-child {
......
...@@ -31,17 +31,11 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -31,17 +31,11 @@ class Admin::ProjectsController < Admin::ApplicationController
protected protected
def project def project
id = params[:project_id] || params[:id] @project = Project.find_with_namespace(params[:id])
@project = Project.find_with_namespace(id)
@project || render_404 @project || render_404
end end
def group def group
@group ||= project.group @group ||= @project.group
end
def repository
@repository ||= project.repository
end end
end end
...@@ -4,6 +4,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -4,6 +4,7 @@ class Admin::UsersController < Admin::ApplicationController
def index def index
@users = User.filter(params[:filter]) @users = User.filter(params[:filter])
@users = @users.search(params[:name]) if params[:name].present? @users = @users.search(params[:name]) if params[:name].present?
@users = @users.sort(@sort = params[:sort])
@users = @users.alphabetically.page(params[:page]) @users = @users.alphabetically.page(params[:page])
end end
......
...@@ -7,7 +7,6 @@ class ApplicationController < ActionController::Base ...@@ -7,7 +7,6 @@ class ApplicationController < ActionController::Base
before_filter :check_password_expiration before_filter :check_password_expiration
before_filter :add_abilities before_filter :add_abilities
before_filter :ldap_security_check before_filter :ldap_security_check
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers before_filter :default_headers
before_filter :add_gon_variables before_filter :add_gon_variables
before_filter :configure_permitted_parameters, if: :devise_controller? before_filter :configure_permitted_parameters, if: :devise_controller?
...@@ -81,6 +80,7 @@ class ApplicationController < ActionController::Base ...@@ -81,6 +80,7 @@ class ApplicationController < ActionController::Base
end end
def project def project
unless @project
id = params[:project_id] || params[:id] id = params[:project_id] || params[:id]
# Redirect from # Redirect from
...@@ -104,6 +104,8 @@ class ApplicationController < ActionController::Base ...@@ -104,6 +104,8 @@ class ApplicationController < ActionController::Base
render_404 and return render_404 and return
end end
end end
@project
end
def repository def repository
@repository ||= project.repository @repository ||= project.repository
...@@ -119,14 +121,6 @@ class ApplicationController < ActionController::Base ...@@ -119,14 +121,6 @@ class ApplicationController < ActionController::Base
return access_denied! unless can?(current_user, action, project) return access_denied! unless can?(current_user, action, project)
end end
def authorize_code_access!
return access_denied! unless can?(current_user, :download_code, project)
end
def authorize_push!
return access_denied! unless can?(current_user, :push_code, project)
end
def authorize_labels! def authorize_labels!
# Labels should be accessible for issues and/or merge requests # Labels should be accessible for issues and/or merge requests
authorize_read_issue! || authorize_read_merge_request! authorize_read_issue! || authorize_read_merge_request!
...@@ -170,9 +164,6 @@ class ApplicationController < ActionController::Base ...@@ -170,9 +164,6 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end end
def dev_tools
end
def default_headers def default_headers
headers['X-Frame-Options'] = 'DENY' headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block' headers['X-XSS-Protection'] = '1; mode=block'
......
...@@ -19,6 +19,7 @@ class Groups::GroupMembersController < ApplicationController ...@@ -19,6 +19,7 @@ class Groups::GroupMembersController < ApplicationController
def destroy def destroy
@users_group = @group.group_members.find(params[:id]) @users_group = @group.group_members.find(params[:id])
if can?(current_user, :destroy, @users_group) # May fail if last owner. if can?(current_user, :destroy, @users_group) # May fail if last owner.
@users_group.destroy @users_group.destroy
respond_to do |format| respond_to do |format|
......
...@@ -15,15 +15,17 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -15,15 +15,17 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
error.to_s.humanize if error error.to_s.humanize if error
end end
def ldap
# We only find ourselves here # We only find ourselves here
# if the authentication to LDAP was successful. # if the authentication to LDAP was successful.
@user = Gitlab::LDAP::User.find_or_create(oauth) def ldap
@user.remember_me = true if @user.persisted? @user = Gitlab::LDAP::User.new(oauth)
@user.save if @user.changed? # will also save new users
gl_user = @user.gl_user
gl_user.remember_me = true if @user.persisted?
# Do additional LDAP checks for the user filter and EE features # Do additional LDAP checks for the user filter and EE features
if Gitlab::LDAP::Access.allowed?(@user) if @user.allowed?
sign_in_and_redirect(@user) sign_in_and_redirect(gl_user)
else else
flash[:alert] = "Access denied for your LDAP account." flash[:alert] = "Access denied for your LDAP account."
redirect_to new_user_session_path redirect_to new_user_session_path
...@@ -46,26 +48,28 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -46,26 +48,28 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
current_user.save current_user.save
redirect_to profile_path redirect_to profile_path
else else
@user = Gitlab::OAuth::User.find(oauth) @user = Gitlab::OAuth::User.new(oauth)
@user.save
# Create user if does not exist
# and allow_single_sign_on is true
if Gitlab.config.omniauth['allow_single_sign_on'] && !@user
@user, errors = Gitlab::OAuth::User.create(oauth)
end
if @user && !errors # Only allow properly saved users to login.
sign_in_and_redirect(@user) if @user.persisted? && @user.valid?
sign_in_and_redirect(@user.gl_user)
else else
if errors error_message =
error_message = errors.map{ |attribute, message| "#{attribute} #{message}" }.join(", ") if @user.gl_user.errors.any?
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return @user.gl_user.errors.map do |attribute, message|
"#{attribute} #{message}"
end.join(", ")
else else
flash[:notice] = "There's no such user!" ''
end end
redirect_to new_user_session_path
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end end
end end
rescue StandardError
flash[:notice] = "There's no such user!"
redirect_to new_user_session_path
end end
def oauth def oauth
......
...@@ -2,7 +2,7 @@ class Projects::BaseTreeController < Projects::ApplicationController ...@@ -2,7 +2,7 @@ class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
end end
...@@ -4,7 +4,7 @@ class Projects::BlameController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::BlameController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -4,9 +4,9 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -4,9 +4,9 @@ class Projects::BlobController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_push!, only: [:destroy] before_filter :authorize_push_code!, only: [:destroy]
before_filter :blob before_filter :blob
...@@ -20,7 +20,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -20,7 +20,7 @@ class Projects::BlobController < Projects::ApplicationController
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to project_tree_path(@project, @ref) redirect_to project_tree_path(@project, @ref)
else else
flash[:alert] = result[:error] flash[:alert] = result[:message]
render :show render :show
end end
end end
......
...@@ -3,8 +3,8 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -3,8 +3,8 @@ class Projects::BranchesController < Projects::ApplicationController
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :authorize_push!, only: [:create, :destroy] before_filter :authorize_push_code!, only: [:create, :destroy]
def index def index
@sort = params[:sort] || 'name' @sort = params[:sort] || 'name'
......
...@@ -4,19 +4,19 @@ ...@@ -4,19 +4,19 @@
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :commit before_filter :commit
def show def show
return git_not_found! unless @commit return git_not_found! unless @commit
@line_notes = project.notes.for_commit_id(commit.id).inline @line_notes = @project.notes.for_commit_id(commit.id).inline
@branches = project.repository.branch_names_contains(commit.id) @branches = @project.repository.branch_names_contains(commit.id)
@diffs = @commit.diffs @diffs = @commit.diffs
@note = project.build_commit_note(commit) @note = @project.build_commit_note(commit)
@notes_count = project.notes.for_commit_id(commit.id).count @notes_count = @project.notes.for_commit_id(commit.id).count
@notes = project.notes.for_commit_id(@commit.id).not_inline.fresh @notes = @project.notes.for_commit_id(@commit.id).not_inline.fresh
@noteable = @commit @noteable = @commit
@comments_allowed = @reply_allowed = true @comments_allowed = @reply_allowed = true
@comments_target = { @comments_target = {
...@@ -32,6 +32,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -32,6 +32,6 @@ class Projects::CommitController < Projects::ApplicationController
end end
def commit def commit
@commit ||= project.repository.commit(params[:id]) @commit ||= @project.repository.commit(params[:id])
end end
end end
...@@ -5,7 +5,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -5,7 +5,7 @@ class Projects::CommitsController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
class Projects::CompareController < Projects::ApplicationController class Projects::CompareController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def index def index
......
...@@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def enable def enable
project.deploy_keys << available_keys.find(params[:id]) @project.deploy_keys << available_keys.find(params[:id])
redirect_to project_deploy_keys_path(@project) redirect_to project_deploy_keys_path(@project)
end end
......
class Projects::EditTreeController < Projects::BaseTreeController class Projects::EditTreeController < Projects::BaseTreeController
before_filter :require_branch_head before_filter :require_branch_head
before_filter :blob before_filter :blob
before_filter :authorize_push! before_filter :authorize_push_code!
before_filter :from_merge_request before_filter :from_merge_request
before_filter :after_edit_path before_filter :after_edit_path
...@@ -22,7 +22,7 @@ class Projects::EditTreeController < Projects::BaseTreeController ...@@ -22,7 +22,7 @@ class Projects::EditTreeController < Projects::BaseTreeController
redirect_to after_edit_path redirect_to after_edit_path
else else
flash[:alert] = result[:error] flash[:alert] = result[:message]
render :show render :show
end end
end end
......
class Projects::GraphsController < Projects::ApplicationController class Projects::GraphsController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
class Projects::NewTreeController < Projects::BaseTreeController class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head before_filter :require_branch_head
before_filter :authorize_push! before_filter :authorize_push_code!
def show def show
end end
......
...@@ -4,7 +4,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::RawController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -3,7 +3,7 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -3,7 +3,7 @@ class Projects::RefsController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def switch def switch
......
class Projects::RepositoriesController < Projects::ApplicationController class Projects::RepositoriesController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def archive def archive
......
...@@ -17,7 +17,10 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -17,7 +17,10 @@ class Projects::SnippetsController < Projects::ApplicationController
respond_to :html respond_to :html
def index def index
@snippets = @project.snippets.fresh.non_expired @snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_project,
project: @project
})
end end
def new def new
...@@ -88,6 +91,6 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -88,6 +91,6 @@ class Projects::SnippetsController < Projects::ApplicationController
end end
def snippet_params def snippet_params
params.require(:project_snippet).permit(:title, :content, :file_name, :private) params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
end end
end end
...@@ -3,8 +3,8 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -3,8 +3,8 @@ class Projects::TagsController < Projects::ApplicationController
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :authorize_push!, only: [:create] before_filter :authorize_push_code!, only: [:create]
before_filter :authorize_admin_project!, only: [:destroy] before_filter :authorize_admin_project!, only: [:destroy]
def index def index
......
...@@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def new def new
@user_project_relation = project.project_members.new @user_project_relation = @project.project_members.new
end end
def create def create
...@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def update def update
@user_project_relation = project.project_members.find_by(user_id: member) @user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.update_attributes(member_params) @user_project_relation.update_attributes(member_params)
unless @user_project_relation.valid? unless @user_project_relation.valid?
...@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def destroy def destroy
@user_project_relation = project.project_members.find_by(user_id: member) @user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.destroy @user_project_relation.destroy
respond_to do |format| respond_to do |format|
...@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def leave def leave
project.project_members.find_by(user_id: current_user).destroy @project.project_members.find_by(user_id: current_user).destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to :back }
......
...@@ -6,7 +6,6 @@ class ProjectsController < ApplicationController ...@@ -6,7 +6,6 @@ class ProjectsController < ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create] before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'navless', only: [:new, :create, :fork] layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create] before_filter :set_title, only: [:new, :create]
...@@ -76,7 +75,7 @@ class ProjectsController < ApplicationController ...@@ -76,7 +75,7 @@ class ProjectsController < ApplicationController
end end
def import def import
if project.import_finished? if @project.import_finished?
redirect_to @project redirect_to @project
return return
end end
...@@ -98,7 +97,7 @@ class ProjectsController < ApplicationController ...@@ -98,7 +97,7 @@ class ProjectsController < ApplicationController
end end
def destroy def destroy
return access_denied! unless can?(current_user, :remove_project, project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
...@@ -148,8 +147,8 @@ class ProjectsController < ApplicationController ...@@ -148,8 +147,8 @@ class ProjectsController < ApplicationController
end end
def archive def archive
return access_denied! unless can?(current_user, :archive_project, project) return access_denied! unless can?(current_user, :archive_project, @project)
project.archive! @project.archive!
respond_to do |format| respond_to do |format|
format.html { redirect_to @project } format.html { redirect_to @project }
...@@ -157,8 +156,8 @@ class ProjectsController < ApplicationController ...@@ -157,8 +156,8 @@ class ProjectsController < ApplicationController
end end
def unarchive def unarchive
return access_denied! unless can?(current_user, :archive_project, project) return access_denied! unless can?(current_user, :archive_project, @project)
project.unarchive! @project.unarchive!
respond_to do |format| respond_to do |format|
format.html { redirect_to @project } format.html { redirect_to @project }
......
...@@ -18,6 +18,10 @@ class SessionsController < Devise::SessionsController ...@@ -18,6 +18,10 @@ class SessionsController < Devise::SessionsController
store_location_for(:redirect, redirect_path) store_location_for(:redirect, redirect_path)
end end
if Gitlab.config.ldap.enabled
@ldap_servers = Gitlab::LDAP::Config.servers
end
super super
end end
......
...@@ -9,12 +9,14 @@ class SnippetsController < ApplicationController ...@@ -9,12 +9,14 @@ class SnippetsController < ApplicationController
before_filter :set_title before_filter :set_title
skip_before_filter :authenticate_user!, only: [:index, :user_index]
respond_to :html respond_to :html
layout 'navless' layout :determine_layout
def index def index
@snippets = Snippet.are_internal.fresh.non_expired.page(params[:page]).per(20) @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(20)
end end
def user_index def user_index
...@@ -22,22 +24,11 @@ class SnippetsController < ApplicationController ...@@ -22,22 +24,11 @@ class SnippetsController < ApplicationController
render_404 and return unless @user render_404 and return unless @user
@snippets = @user.snippets.fresh.non_expired @snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_user,
if @user == current_user user: @user,
@snippets = case params[:scope] scope: params[:scope]}).
when 'are_internal' then page(params[:page]).per(20)
@snippets.are_internal
when 'are_private' then
@snippets.are_private
else
@snippets
end
else
@snippets = @snippets.are_internal
end
@snippets = @snippets.page(params[:page]).per(20)
if @user == current_user if @user == current_user
render 'current_user_index' render 'current_user_index'
...@@ -95,7 +86,14 @@ class SnippetsController < ApplicationController ...@@ -95,7 +86,14 @@ class SnippetsController < ApplicationController
protected protected
def snippet def snippet
@snippet ||= PersonalSnippet.where('author_id = :user_id or private is false', user_id: current_user.id).find(params[:id]) @snippet ||= if current_user
PersonalSnippet.where("author_id = ? OR visibility_level IN (?)",
current_user.id,
[Snippet::PUBLIC, Snippet::INTERNAL]).
find(params[:id])
else
PersonalSnippet.are_public.find(params[:id])
end
end end
def authorize_modify_snippet! def authorize_modify_snippet!
...@@ -111,6 +109,10 @@ class SnippetsController < ApplicationController ...@@ -111,6 +109,10 @@ class SnippetsController < ApplicationController
end end
def snippet_params def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private) params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
end
def determine_layout
current_user ? 'navless' : 'public_users'
end end
end end
class SnippetsFinder
def execute(current_user, params = {})
filter = params[:filter]
case filter
when :all then
snippets(current_user).fresh.non_expired
when :by_user then
by_user(current_user, params[:user], params[:scope])
when :by_project
by_project(current_user, params[:project])
end
end
private
def snippets(current_user)
if current_user
Snippet.public_and_internal
else
# Not authenticated
#
# Return only:
# public snippets
Snippet.are_public
end
end
def by_user(current_user, user, scope)
snippets = user.snippets.fresh.non_expired
if user == current_user
case scope
when 'are_internal' then
snippets.are_internal
when 'are_private' then
snippets.are_private
when 'are_public' then
snippets.are_public
else
snippets
end
else
snippets.public_and_internal
end
end
def by_project(current_user, project)
snippets = project.snippets.fresh.non_expired
if current_user
if project.team.member?(current_user.id)
snippets
else
snippets.public_and_internal
end
else
snippets.are_public
end
end
end
...@@ -120,4 +120,8 @@ module CommitsHelper ...@@ -120,4 +120,8 @@ module CommitsHelper
class: 'commit-short-id') class: 'commit-short-id')
end end
end end
def truncate_sha(sha)
Commit.truncate_sha(sha)
end
end end
...@@ -136,9 +136,8 @@ module EventsHelper ...@@ -136,9 +136,8 @@ module EventsHelper
end end
def event_note(text) def event_note(text)
text = first_line_in_markdown(text) text = first_line_in_markdown(text, 150)
text = truncate(text, length: 150) sanitize(text, tags: %w(a img b pre code p))
sanitize(markdown(text), tags: %w(a img b pre p))
end end
def event_commit_title(message) def event_commit_title(message)
......
...@@ -51,12 +51,14 @@ module GitlabMarkdownHelper ...@@ -51,12 +51,14 @@ module GitlabMarkdownHelper
@markdown.render(text).html_safe @markdown.render(text).html_safe
end end
def first_line_in_markdown(text) # Return the first line of +text+, up to +max_chars+, after parsing the line
line = text.split("\n").detect do |i| # as Markdown. HTML tags in the parsed output are not counted toward the
i.present? && markdown(i).present? # +max_chars+ limit. If the length limit falls within a tag's contents, then
end # the tag contents are truncated without removing the closing tag.
line += '...' unless line.nil? def first_line_in_markdown(text, max_chars = nil)
line md = markdown(text).strip
truncate_visible(md, max_chars || md.length) if md.present?
end end
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
...@@ -204,4 +206,52 @@ module GitlabMarkdownHelper ...@@ -204,4 +206,52 @@ module GitlabMarkdownHelper
def correct_ref def correct_ref
@ref ? @ref : "master" @ref ? @ref : "master"
end end
private
# Return +text+, truncated to +max_chars+ characters, excluding any HTML
# tags.
def truncate_visible(text, max_chars)
doc = Nokogiri::HTML.fragment(text)
content_length = 0
truncated = false
doc.traverse do |node|
if node.text? || node.content.empty?
if truncated
node.remove
next
end
# Handle line breaks within a node
if node.content.strip.lines.length > 1
node.content = "#{node.content.lines.first.chomp}..."
truncated = true
end
num_remaining = max_chars - content_length
if node.content.length > num_remaining
node.content = node.content.truncate(num_remaining)
truncated = true
end
content_length += node.content.length
end
truncated = truncate_if_block(node, truncated)
end
doc.to_html
end
# Used by #truncate_visible. If +node+ is the first block element, and the
# text hasn't already been truncated, then append "..." to the node contents
# and return true. Otherwise return false.
def truncate_if_block(node, truncated)
if node.element? && node.description.block? && !truncated
node.content = "#{node.content}..." if node.next_sibling
true
else
truncated
end
end
end end
module OauthHelper module OauthHelper
def ldap_enabled? def ldap_enabled?
Devise.omniauth_providers.include?(:ldap) Gitlab.config.ldap.enabled
end end
def default_providers def default_providers
......
...@@ -28,6 +28,23 @@ module VisibilityLevelHelper ...@@ -28,6 +28,23 @@ module VisibilityLevelHelper
end end
end end
def snippet_visibility_level_description(level)
capture_haml do
haml_tag :span do
case level
when Gitlab::VisibilityLevel::PRIVATE
haml_concat "The snippet is visible only for me"
when Gitlab::VisibilityLevel::INTERNAL
haml_concat "The snippet is visible for any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
haml_concat "The snippet can be accessed"
haml_concat "without any"
haml_concat "authentication."
end
end
end
end
def visibility_level_icon(level) def visibility_level_icon(level)
case level case level
when Gitlab::VisibilityLevel::PRIVATE when Gitlab::VisibilityLevel::PRIVATE
......
...@@ -19,13 +19,24 @@ class Commit ...@@ -19,13 +19,24 @@ class Commit
class << self class << self
def decorate(commits) def decorate(commits)
commits.map { |c| self.new(c) } commits.map do |commit|
if commit.kind_of?(Commit)
commit
else
self.new(commit)
end
end
end end
# Calculate number of lines to render for diffs # Calculate number of lines to render for diffs
def diff_line_count(diffs) def diff_line_count(diffs)
diffs.reduce(0) { |sum, d| sum + d.diff.lines.count } diffs.reduce(0) { |sum, d| sum + d.diff.lines.count }
end end
# Truncate sha to 8 characters
def truncate_sha(sha)
sha[0..7]
end
end end
attr_accessor :raw attr_accessor :raw
...@@ -111,7 +122,7 @@ class Commit ...@@ -111,7 +122,7 @@ class Commit
# Mentionable override. # Mentionable override.
def gfm_reference def gfm_reference
"commit #{sha[0..5]}" "commit #{id}"
end end
def method_missing(m, *args, &block) def method_missing(m, *args, &block)
...@@ -124,6 +135,11 @@ class Commit ...@@ -124,6 +135,11 @@ class Commit
super super
end end
# Truncate sha to 8 characters
def short_id
@raw.short_id(7)
end
def parents def parents
@parents ||= Commit.decorate(super) @parents ||= Commit.decorate(super)
end end
......
...@@ -266,7 +266,7 @@ class Event < ActiveRecord::Base ...@@ -266,7 +266,7 @@ class Event < ActiveRecord::Base
end end
def note_short_commit_id def note_short_commit_id
note_commit_id[0..8] Commit.truncate_sha(note_commit_id)
end end
def note_commit? def note_commit?
......
# == Schema Information
#
# Table name: members
#
# id :integer not null, primary key
# access_level :integer not null
# source_id :integer not null
# source_type :string(255) not null
# user_id :integer not null
# notification_level :integer not null
# type :string(255)
# created_at :datetime
# updated_at :datetime
#
class Member < ActiveRecord::Base class Member < ActiveRecord::Base
include Notifiable include Notifiable
include Gitlab::Access include Gitlab::Access
......
# == Schema Information
#
# Table name: members
#
# id :integer not null, primary key
# access_level :integer not null
# source_id :integer not null
# source_type :string(255) not null
# user_id :integer not null
# notification_level :integer not null
# type :string(255)
# created_at :datetime
# updated_at :datetime
#
class GroupMember < Member class GroupMember < Member
SOURCE_TYPE = 'Namespace' SOURCE_TYPE = 'Namespace'
......
# == Schema Information
#
# Table name: members
#
# id :integer not null, primary key
# access_level :integer not null
# source_id :integer not null
# source_type :string(255) not null
# user_id :integer not null
# notification_level :integer not null
# type :string(255)
# created_at :datetime
# updated_at :datetime
#
class ProjectMember < Member class ProjectMember < Member
SOURCE_TYPE = 'Project' SOURCE_TYPE = 'Project'
......
...@@ -55,7 +55,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -55,7 +55,7 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def last_commit_short_sha def last_commit_short_sha
@last_commit_short_sha ||= last_commit.sha[0..10] @last_commit_short_sha ||= last_commit.short_id
end end
private private
......
...@@ -80,7 +80,7 @@ class Note < ActiveRecord::Base ...@@ -80,7 +80,7 @@ class Note < ActiveRecord::Base
note_options = { note_options = {
project: project, project: project,
author: author, author: author,
note: "_mentioned in #{gfm_reference}_", note: cross_reference_note_content(gfm_reference),
system: true system: true
} }
...@@ -174,7 +174,7 @@ class Note < ActiveRecord::Base ...@@ -174,7 +174,7 @@ class Note < ActiveRecord::Base
where(noteable_id: noteable.id) where(noteable_id: noteable.id)
end end
notes.where('note like ?', "_mentioned in #{gfm_reference}_"). notes.where('note like ?', cross_reference_note_content(gfm_reference)).
system.any? system.any?
end end
...@@ -182,8 +182,16 @@ class Note < ActiveRecord::Base ...@@ -182,8 +182,16 @@ class Note < ActiveRecord::Base
where("note like :query", query: "%#{query}%") where("note like :query", query: "%#{query}%")
end end
def cross_reference_note_prefix
'_mentioned in '
end
private private
def cross_reference_note_content(gfm_reference)
cross_reference_note_prefix + "#{gfm_reference}_"
end
# Prepend the mentioner's namespaced project path to the GFM reference for # Prepend the mentioner's namespaced project path to the GFM reference for
# cross-project references. For same-project references, return the # cross-project references. For same-project references, return the
# unmodified GFM reference. # unmodified GFM reference.
...@@ -249,6 +257,10 @@ class Note < ActiveRecord::Base ...@@ -249,6 +257,10 @@ class Note < ActiveRecord::Base
nil nil
end end
def cross_reference?
note.start_with?(self.class.cross_reference_note_prefix)
end
def find_diff def find_diff
return nil unless noteable && noteable.diffs.present? return nil unless noteable && noteable.diffs.present?
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
# updated_at :datetime # updated_at :datetime
# file_name :string(255) # file_name :string(255)
# expires_at :datetime # expires_at :datetime
# private :boolean default(TRUE), not null
# type :string(255) # type :string(255)
# visibility_level :integer default(0), not null
# #
class PersonalSnippet < Snippet class PersonalSnippet < Snippet
......
...@@ -173,7 +173,7 @@ class Project < ActiveRecord::Base ...@@ -173,7 +173,7 @@ class Project < ActiveRecord::Base
end end
def with_push def with_push
includes(:events).where('events.action = ?', Event::PUSHED) joins(:events).where('events.action = ?', Event::PUSHED)
end end
def active def active
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# property :text # properties :text
# #
class BuildboxService < CiService class BuildboxService < CiService
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# property :text # properties :text
# #
class GitlabCiService < CiService class GitlabCiService < CiService
......
...@@ -40,7 +40,8 @@ class SlackService < Service ...@@ -40,7 +40,8 @@ class SlackService < Service
project_name: project_name project_name: project_name
)) ))
credentials = webhook.match(/(\w*).slack.com.*services\/(.*)/) credentials = webhook.match(/([\w-]*).slack.com.*services\/(.*)/)
if credentials.present? if credentials.present?
subdomain = credentials[1] subdomain = credentials[1]
token = credentials[2].split("token=").last token = credentials[2].split("token=").last
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
# updated_at :datetime # updated_at :datetime
# file_name :string(255) # file_name :string(255)
# expires_at :datetime # expires_at :datetime
# private :boolean default(TRUE), not null
# type :string(255) # type :string(255)
# visibility_level :integer default(0), not null
# #
class ProjectSnippet < Snippet class ProjectSnippet < Snippet
......
...@@ -133,6 +133,10 @@ class ProjectTeam ...@@ -133,6 +133,10 @@ class ProjectTeam
max_tm_access(user.id) == Gitlab::Access::MASTER max_tm_access(user.id) == Gitlab::Access::MASTER
end end
def member?(user_id)
!!find_tm(user_id)
end
def max_tm_access(user_id) def max_tm_access(user_id)
access = [] access = []
access << project.project_members.find_by(user_id: user_id).try(:access_field) access << project.project_members.find_by(user_id: user_id).try(:access_field)
......
...@@ -30,6 +30,8 @@ class Repository ...@@ -30,6 +30,8 @@ class Repository
commit = Gitlab::Git::Commit.find(raw_repository, id) commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit) if commit commit = Commit.new(commit) if commit
commit commit
rescue Rugged::OdbError => ex
nil
end end
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false) def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
#
# To add new service you should build a class inherited from Service # To add new service you should build a class inherited from Service
# and implement a set of methods # and implement a set of methods
......
...@@ -11,14 +11,15 @@ ...@@ -11,14 +11,15 @@
# updated_at :datetime # updated_at :datetime
# file_name :string(255) # file_name :string(255)
# expires_at :datetime # expires_at :datetime
# private :boolean default(TRUE), not null
# type :string(255) # type :string(255)
# visibility_level :integer default(0), not null
# #
class Snippet < ActiveRecord::Base class Snippet < ActiveRecord::Base
include Linguist::BlobHelper include Linguist::BlobHelper
include Gitlab::VisibilityLevel
default_value_for :private, true default_value_for :visibility_level, Snippet::PRIVATE
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
...@@ -30,10 +31,13 @@ class Snippet < ActiveRecord::Base ...@@ -30,10 +31,13 @@ class Snippet < ActiveRecord::Base
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
validates :file_name, presence: true, length: { within: 0..255 } validates :file_name, presence: true, length: { within: 0..255 }
validates :content, presence: true validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
# Scopes # Scopes
scope :are_internal, -> { where(private: false) } scope :are_internal, -> { where(visibility_level: Snippet::INTERNAL) }
scope :are_private, -> { where(private: true) } scope :are_private, -> { where(visibility_level: Snippet::PRIVATE) }
scope :are_public, -> { where(visibility_level: Snippet::PUBLIC) }
scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) }
scope :fresh, -> { order("created_at DESC") } scope :fresh, -> { order("created_at DESC") }
scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) } scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) } scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
...@@ -66,6 +70,10 @@ class Snippet < ActiveRecord::Base ...@@ -66,6 +70,10 @@ class Snippet < ActiveRecord::Base
expires_at && expires_at < Time.current expires_at && expires_at < Time.current
end end
def visibility_level_field
visibility_level
end
class << self class << self
def search(query) def search(query)
where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%") where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%")
...@@ -76,7 +84,7 @@ class Snippet < ActiveRecord::Base ...@@ -76,7 +84,7 @@ class Snippet < ActiveRecord::Base
end end
def accessible_to(user) def accessible_to(user)
where('private = ? OR author_id = ?', false, user) where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user)
end end
end end
end end
...@@ -178,8 +178,7 @@ class User < ActiveRecord::Base ...@@ -178,8 +178,7 @@ class User < ActiveRecord::Base
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
scope :ldap, -> { where(provider: 'ldap') } scope :ldap, -> { where('provider LIKE ?', 'ldap%') }
scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
# #
...@@ -196,6 +195,16 @@ class User < ActiveRecord::Base ...@@ -196,6 +195,16 @@ class User < ActiveRecord::Base
end end
end end
def sort(method)
case method.to_s
when 'recent_sign_in' then reorder('users.last_sign_in_at DESC')
when 'oldest_sign_in' then reorder('users.last_sign_in_at ASC')
when 'recently_created' then reorder('users.created_at DESC')
when 'late_created' then reorder('users.created_at ASC')
else reorder("users.name ASC")
end
end
def find_for_commit(email, name) def find_for_commit(email, name)
# Prefer email match over name match # Prefer email match over name match
User.where(email: email).first || User.where(email: email).first ||
...@@ -397,7 +406,7 @@ class User < ActiveRecord::Base ...@@ -397,7 +406,7 @@ class User < ActiveRecord::Base
end end
def ldap_user? def ldap_user?
extern_uid && provider == 'ldap' extern_uid && provider.start_with?('ldap')
end end
def accessible_deploy_keys def accessible_deploy_keys
......
...@@ -10,12 +10,6 @@ module Files ...@@ -10,12 +10,6 @@ module Files
private private
def success
out = super()
out[:error] = ''
out
end
def repository def repository
project.repository project.repository
end end
......
...@@ -33,12 +33,5 @@ module Issues ...@@ -33,12 +33,5 @@ module Issues
issue issue
end end
private
def update_task(issue, params, checked)
issue.update_nth_task(params[:task_num].to_i, checked)
params.except!(:task_num)
end
end end
end end
...@@ -119,7 +119,7 @@ class NotificationService ...@@ -119,7 +119,7 @@ class NotificationService
# ignore gitlab service messages # ignore gitlab service messages
return true if note.note =~ /\A_Status changed to closed_/ return true if note.note =~ /\A_Status changed to closed_/
return true if note.note =~ /\A_mentioned in / && note.system == true return true if note.cross_reference? && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id } opts = { noteable_type: note.noteable_type, project_id: note.project_id }
......
...@@ -2,39 +2,20 @@ ...@@ -2,39 +2,20 @@
- if @group.errors.any? - if @group.errors.any?
.alert.alert-danger .alert.alert-danger
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.form-group.group_name_holder
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Example Group", class: "form-control"
.form-group.group-description-holder = render 'shared/group_form', f: f
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
.form-group.group-description-holder .form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label' = f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10 .col-sm-10
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button = render 'shared/choose_group_avatar_button', f: f
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
- if @group.new_record? - if @group.new_record?
.form-group .form-group
.col-sm-2 .col-sm-2
.col-sm-10 .col-sm-10
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%ul = render 'shared/group_tips'
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
.form-actions .form-actions
= f.submit 'Create group', class: "btn btn-create" = f.submit 'Create group', class: "btn btn-create"
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
......
...@@ -74,13 +74,13 @@ ...@@ -74,13 +74,13 @@
%ul.well-list.group-users-list %ul.well-list.group-users-list
- @members.each do |member| - @members.each do |member|
- user = member.user - user = member.user
%li{class: dom_class(user)} %li{class: dom_class(member), id: dom_id(user)}
.list-item-name .list-item-name
%strong %strong
= link_to user.name, admin_user_path(user) = link_to user.name, admin_user_path(user)
%span.pull-right.light %span.pull-right.light
= member.human_access = member.human_access
= link_to group_group_members_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse %i.fa.fa-minus.fa-inverse
.panel-footer .panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab' = paginate @members, param_name: 'members_page', theme: 'gitlab'
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
%ul.nav.nav-tabs.log-tabs %ul.nav.nav-tabs.log-tabs
%li.active - loggers.each do |klass|
= link_to "githost.log", "#githost", 'data-toggle' => 'tab' %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') }
%li = link_to klass::file_name, "##{klass::file_name_noext}",
= link_to "application.log", "#application", 'data-toggle' => 'tab' 'data-toggle' => 'tab'
%li
= link_to "production.log", "#production", 'data-toggle' => 'tab'
%li
= link_to "sidekiq.log", "#sidekiq", 'data-toggle' => 'tab'
%p.light To prevent performance issues admin logs output the last 2000 lines %p.light To prevent performance issues admin logs output the last 2000 lines
.tab-content .tab-content
.tab-pane.active#githost - loggers.each do |klass|
.file-holder#README .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''),
.file-title id: klass::file_name_noext }
%i.fa.fa-file
githost.log
.pull-right
= link_to '#', class: 'log-bottom' do
%i.fa.fa-arrow-down
Scroll down
.file-content.logs
%ol
- Gitlab::GitLogger.read_latest.each do |line|
%li
%p= line
.tab-pane#application
.file-holder#README
.file-title
%i.fa.fa-file
application.log
.pull-right
= link_to '#', class: 'log-bottom' do
%i.fa.fa-arrow-down
Scroll down
.file-content.logs
%ol
- Gitlab::AppLogger.read_latest.each do |line|
%li
%p= line
.tab-pane#production
.file-holder#README
.file-title
%i.fa.fa-file
production.log
.pull-right
= link_to '#', class: 'log-bottom' do
%i.fa.fa-arrow-down
Scroll down
.file-content.logs
%ol
- Gitlab::Logger.read_latest_for('production.log').each do |line|
%li
%p= line
.tab-pane#sidekiq
.file-holder#README .file-holder#README
.file-title .file-title
%i.fa.fa-file %i.fa.fa-file
sidekiq.log = klass::file_name
.pull-right .pull-right
= link_to '#', class: 'log-bottom' do = link_to '#', class: 'log-bottom' do
%i.fa.fa-arrow-down %i.fa.fa-arrow-down
Scroll down Scroll down
.file-content.logs .file-content.logs
%ol %ol
- Gitlab::Logger.read_latest_for('sidekiq.log').each do |line| - klass.read_latest.each do |line|
%li %li
%p= line %p= line
...@@ -32,6 +32,26 @@ ...@@ -32,6 +32,26 @@
.panel-heading .panel-heading
Users (#{@users.total_count}) Users (#{@users.total_count})
.panel-head-actions .panel-head-actions
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
= @sort.humanize
- else
Name
%b.caret
%ul.dropdown-menu
%li
= link_to admin_users_path(sort: nil) do
Name
= link_to admin_users_path(sort: 'recent_sign_in') do
Recent sign in
= link_to admin_users_path(sort: 'oldest_sign_in') do
Oldest sign in
= link_to admin_users_path(sort: 'recently_created') do
Recently created
= link_to admin_users_path(sort: 'late_created') do
Late created
= link_to 'New User', new_admin_user_path, class: "btn btn-new" = link_to 'New User', new_admin_user_path, class: "btn btn-new"
%ul.well-list %ul.well-list
- @users.each do |user| - @users.each do |user|
......
...@@ -46,5 +46,5 @@ ...@@ -46,5 +46,5 @@
%br %br
Public projects are an easy way to allow everyone to have read-only access. Public projects are an easy way to allow everyone to have read-only access.
.link_holder .link_holder
= link_to explore_projects_path, class: "btn btn-new" do = link_to trending_explore_projects_path, class: "btn btn-new" do
Browse public projects » Browse public projects »
= form_tag(user_omniauth_callback_path(:ldap), id: 'new_ldap_user' ) do = form_tag(user_omniauth_callback_path(provider), id: 'new_ldap_user' ) do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"} = 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"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
%br/ %br/
......
...@@ -2,22 +2,24 @@ ...@@ -2,22 +2,24 @@
.login-heading .login-heading
%h3 Sign in %h3 Sign in
.login-body .login-body
- if ldap_enabled? && gitlab_config.signin_enabled - if ldap_enabled?
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li.active - @ldap_servers.each_with_index do |server, i|
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab' %li{class: (:active if i.zero?)}
= link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
- if gitlab_config.signin_enabled
%li %li
= link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
.tab-content .tab-content
%div#tab-ldap.tab-pane.active - @ldap_servers.each_with_index do |server, i|
= render partial: 'devise/sessions/new_ldap' %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero?)}
= render 'devise/sessions/new_ldap', provider: server['provider_name']
- if gitlab_config.signin_enabled
%div#tab-signin.tab-pane %div#tab-signin.tab-pane
= render partial: 'devise/sessions/new_base' = render 'devise/sessions/new_base'
- elsif ldap_enabled?
= render partial: 'devise/sessions/new_ldap'
- elsif gitlab_config.signin_enabled - elsif gitlab_config.signin_enabled
= render partial: 'devise/sessions/new_base' = render 'devise/sessions/new_base'
- else - else
%div %div
No authentication methods configured. No authentication methods configured.
...@@ -36,7 +38,6 @@ ...@@ -36,7 +38,6 @@
%span.light Did not receive confirmation email? %span.light Did not receive confirmation email?
= link_to "Send again", new_confirmation_path(resource_name) = link_to "Send again", new_confirmation_path(resource_name)
- if extra_config.has_key?('sign_in_text') - if extra_config.has_key?('sign_in_text')
%hr %hr
= markdown(extra_config.sign_in_text) = markdown(extra_config.sign_in_text)
%li.commit %li.commit
.commit-row-title .commit-row-title
= link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: '' = link_to truncate_sha(commit[:id]), project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp; &nbsp;
= gfm event_commit_title(commit[:message]), project = gfm event_commit_title(commit[:message]), project
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- event.commits.first(15).each do |commit| - event.commits.first(15).each do |commit|
%p %p
%strong= commit[:author][:name] %strong= commit[:author][:name]
= link_to "(##{commit[:id][0...8]})", project_commit_path(event.project, id: commit[:id]) = link_to "(##{truncate_sha(commit[:id])})", project_commit_path(event.project, id: commit[:id])
%i %i
at at
= commit[:timestamp].to_time.to_s(:short) = commit[:timestamp].to_time.to_s(:short)
......
...@@ -22,4 +22,4 @@ ...@@ -22,4 +22,4 @@
- if event.commits_count > 2 - if event.commits_count > 2
%span ... and #{event.commits_count - 2} more commits. %span ... and #{event.commits_count - 2} more commits.
= link_to project_compare_path(event.project, from: event.commit_from, to: event.commit_to) do = link_to project_compare_path(event.project, from: event.commit_from, to: event.commit_to) do
%strong Compare &rarr; #{event.commit_from[0..7]}...#{event.commit_to[0..7]} %strong Compare &rarr; #{truncate_sha(event.commit_from)}...#{truncate_sha(event.commit_to)}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
- if current_page?(starred_explore_projects_path) - if current_page?(starred_explore_projects_path)
%strong.pull-right %strong.pull-right
%i.fa.fa-star
= pluralize project.star_count, 'star' = pluralize project.star_count, 'star'
.project-info .project-info
......
.explore-trending-block .explore-trending-block
%p.lead %p.lead
%i.fa.fa-comments-o %i.fa.fa-star
See most starred projects See most starred projects
%hr %hr
.public-projects .public-projects
......
...@@ -11,16 +11,7 @@ ...@@ -11,16 +11,7 @@
- if @group.errors.any? - if @group.errors.any?
.alert.alert-danger .alert.alert-danger
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.form-group = render 'shared/group_form', f: f
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left"
.form-group.group-description-holder
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
.form-group .form-group
.col-sm-2 .col-sm-2
...@@ -31,13 +22,7 @@ ...@@ -31,13 +22,7 @@
You can change your group avatar here You can change your group avatar here
- else - else
You can upload a group avatar here You can upload a group avatar here
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button = render 'shared/choose_group_avatar_button', f: f
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
- if @group.avatar? - if @group.avatar?
%hr %hr
= link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
......
...@@ -2,37 +2,18 @@ ...@@ -2,37 +2,18 @@
- if @group.errors.any? - if @group.errors.any?
.alert.alert-danger .alert.alert-danger
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.form-group
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control", tabindex: 1, autofocus: true
.form-group.group-description-holder = render 'shared/group_form', f: f, autofocus: true
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4, tabindex: 2
.form-group.group-description-holder .form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label' = f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10 .col-sm-10
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button = render 'shared/choose_group_avatar_button', f: f
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
.form-group .form-group
.col-sm-2 .col-sm-2
.col-sm-10 .col-sm-10
%ul = render 'shared/group_tips'
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
.form-actions .form-actions
= f.submit 'Create group', class: "btn btn-create", tabindex: 3 = f.submit 'Create group', class: "btn btn-create", tabindex: 3
...@@ -31,12 +31,12 @@ ...@@ -31,12 +31,12 @@
.clearfix .clearfix
%hr %hr
%p
You can also specify notification level per group or per project
%br
By default all projects and groups uses notification level set above
.row.all-notifications .row.all-notifications
.col-md-6 .col-md-6
%p
You can also specify notification level per group or per project.
%br
By default all projects and groups uses notification level set above.
%h4 Groups: %h4 Groups:
%ul.bordered-list %ul.bordered-list
- @group_members.each do |users_group| - @group_members.each do |users_group|
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
= render 'settings', type: 'group', membership: users_group, notification: notification = render 'settings', type: 'group', membership: users_group, notification: notification
.col-md-6 .col-md-6
%p
To specify notification level per project of a group you belong to,
%br
you need to be a member of the project itself, not only its group.
%h4 Projects: %h4 Projects:
%ul.bordered-list %ul.bordered-list
- @project_members.each do |project_member| - @project_members.each do |project_member|
......
.form-actions
.commit-button-annotation
= button_tag 'Commit Changes',
class: 'btn commit-btn js-commit-button btn-create'
.message
to branch
%strong= ref
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
%tr %tr
%td.blame-commit %td.blame-commit
%span.commit %span.commit
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id, project_commit_path(@project, commit), class: "commit_short_id"
&nbsp; &nbsp;
= commit_author_link(commit, avatar: true, size: 16) = commit_author_link(commit, avatar: true, size: 16)
&nbsp; &nbsp;
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
.commit-info-row .commit-info-row
%span.cgray= pluralize(@commit.parents.count, "parent") %span.cgray= pluralize(@commit.parents.count, "parent")
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
= link_to parent.id[0...10], project_commit_path(@project, parent) = link_to parent.short_id, project_commit_path(@project, parent)
- if @branches.any? - if @branches.any?
.commit-info-row .commit-info-row
......
%li.commit.js-toggle-container %li.commit.js-toggle-container
.commit-row-title .commit-row-title
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id"
&nbsp; &nbsp;
%span.str-truncated %span.str-truncated
= link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message"
......
%li.commit.inline-commit %li.commit.inline-commit
.commit-row-title .commit-row-title
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id"
&nbsp; &nbsp;
%span.str-truncated %span.str-truncated
= link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message"
......
...@@ -23,16 +23,11 @@ ...@@ -23,16 +23,11 @@
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
= render 'shared/commit_message_container', params: params, = render 'shared/commit_message_container', params: params,
placeholder: "Update #{@blob.name}" placeholder: "Update #{@blob.name}"
.form-actions
= hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
.commit-button-annotation = render 'projects/commit_button', ref: @ref,
= button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-primary' cancel_path: @after_edit_path
.message
to branch
%strong= @ref
= link_to "Cancel", @after_edit_path, class: "btn btn-cancel", data: { confirm: leave_edit_message}
:javascript :javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace") ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace")
......
...@@ -19,12 +19,13 @@ ...@@ -19,12 +19,13 @@
= form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f| = form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
.form-group.import-url-data .form-group.import-url-data
= f.label :import_url, class: 'control-label' do = f.label :import_url, class: 'control-label' do
%span Import existing repo %span Import existing git repo
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br %br
The import will time out after 4 minutes. For big repositories, use a clone/push combination. The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
.form-actions .form-actions
= f.submit 'Retry import', class: "btn btn-create", tabindex: 4 = f.submit 'Retry import', class: "btn btn-create", tabindex: 4
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
= hidden_field_tag :issue_context = hidden_field_tag :issue_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif issue.milestone - elsif issue.milestone
= link_to issue.milestone.title, project_milestone_path = link_to project_milestone_path(@project, @issue.milestone) do
= @issue.milestone.title
- else - else
None None
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
- content_for :note_actions do - content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
- unless @merge_request.closed? || @merge_request.merged? - if @merge_request.open?
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
- if @merge_request.closed? - if @merge_request.closed?
= link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
......
...@@ -16,15 +16,6 @@ ...@@ -16,15 +16,6 @@
%h4 %h4
You can accept this request automatically. You can accept this request automatically.
.accept-merge-holder.clearfix .accept-merge-holder.clearfix
.js-toggle-container
%p
You can
%strong= link_to "modify merge commit message", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message"
before accepting merge request
.js-toggle-content.hide
= render 'shared/commit_message_container', params: params,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
.accept-group .accept-group
.pull-left .pull-left
= f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
...@@ -33,6 +24,14 @@ ...@@ -33,6 +24,14 @@
= label_tag :should_remove_source_branch, class: "checkbox" do = label_tag :should_remove_source_branch, class: "checkbox" do
= check_box_tag :should_remove_source_branch = check_box_tag :should_remove_source_branch
Remove source-branch Remove source-branch
.js-toggle-container
%label
%i.fa.fa-edit
= link_to "modify merge commit message", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message"
.js-toggle-content.hide
= render 'shared/commit_message_container', params: params,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
%hr %hr
.light .light
......
...@@ -21,6 +21,12 @@ ...@@ -21,6 +21,12 @@
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= render "projects/merge_requests/show/remove_source_branch" = render "projects/merge_requests/show/remove_source_branch"
- if @merge_request.locked?
%h4
Merge in progress...
%p
GitLab tries to merge it right now. During this time merge request is locked and can not be closed.
- unless @commits.any? - unless @commits.any?
%h4 Nothing to merge %h4 Nothing to merge
%p %p
......
...@@ -44,13 +44,14 @@ ...@@ -44,13 +44,14 @@
.js-toggle-content.hide .js-toggle-content.hide
.form-group.import-url-data .form-group.import-url-data
= f.label :import_url, class: 'control-label' do = f.label :import_url, class: 'control-label' do
%span Import existing repo %span Import existing git repo
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br %br
The import will time out after 4 minutes. For big repositories, use a clone/push combination. The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
%hr %hr
.form-group .form-group
......
...@@ -27,14 +27,9 @@ ...@@ -27,14 +27,9 @@
.file-content.code .file-content.code
%pre#editor= params[:content] %pre#editor= params[:content]
.form-actions = hidden_field_tag 'content', '', id: 'file-content'
= hidden_field_tag 'content', '', id: "file-content" = render 'projects/commit_button', ref: @ref,
.commit-button-annotation cancel_path: project_tree_path(@project, @id)
= button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-create'
.message
to branch
%strong= @ref
= link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", data: { confirm: leave_edit_message}
:javascript :javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict") ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
- else - else
%td.old_line= raw(line.type == "new" ? "&nbsp;" : line.old_pos) %td.old_line= raw(line.type == "new" ? "&nbsp;" : line.old_pos)
%td.new_line= raw(line.type == "old" ? "&nbsp;" : line.new_pos) %td.new_line= raw(line.type == "old" ? "&nbsp;" : line.new_pos)
%td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw "#{line.text} &nbsp;" %td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
- if line_code == note.line_code - if line_code == note.line_code
= render "projects/notes/diff_notes_with_reply", notes: discussion_notes = render "projects/notes/diff_notes_with_reply", notes: discussion_notes
%h3.page-title Protected branches %h3.page-title Protected branches
%p.light This ability keeps stable branches secure and forces developers to use code reviews %p.light Keep stable branches secure and force developers to use Merge Requests
%hr %hr
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%p Protected branches are designed to %p Protected branches are designed to
%ul %ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"} %li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
%li prevents anyone from force pushing to the branch %li prevent anyone from force pushing to the branch
%li prevents anyone from deleting the branch %li prevent anyone from deleting the branch
%p Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"} %p Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
@ @
%span.monospace %span.monospace
- if commit.nil? - if commit.nil?
#{submodule_item.id[0..10]} #{truncate_sha(submodule_item.id)}
- else - else
= link_to "#{submodule_item.id[0..10]}", commit = link_to "#{truncate_sha(submodule_item.id)}", commit
%td %td
%td.hidden-xs %td.hidden-xs
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
%tr %tr
%td %td
= link_to project_wiki_path(@project, @page, version_id: commit.id) do = link_to project_wiki_path(@project, @page, version_id: commit.id) do
= commit.id[0..10] = truncate_sha(commit.id)
%td %td
= commit.author.name = commit.author.name
%td %td
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
= project.name_with_namespace = project.name_with_namespace
&middot; &middot;
= link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do = link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do
Commit #{note.commit_id[0..8]} Commit #{truncate_sha(note.commit_id)}
- else - else
= link_to project do = link_to project do
= project.name_with_namespace = project.name_with_namespace
......
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: 'js-group-avatar-input hidden'
.light The maximum file size allowed is 100KB.
.form-group
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: 'Example Group', class: 'form-control',
autofocus: local_assigns[:autofocus] || false
.form-group.group-description-holder
= f.label :description, 'Details', class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250,
class: 'form-control js-gfm-input', rows: 4
%ul
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
...@@ -10,21 +10,7 @@ ...@@ -10,21 +10,7 @@
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
.col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true
- unless @snippet.respond_to?(:project) = render "shared/snippets/visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
.form-group
= f.label "Access", class: 'control-label'
.col-sm-10
= f.label :private_true, class: 'radio-label' do
= f.radio_button :private, true
%span
%strong Private
(only you can see this snippet)
%br
= f.label :private_false, class: 'radio-label' do
= f.radio_button :private, false
%span
%strong Internal
(GitLab users can see this snippet)
.form-group .form-group
.file-editor .file-editor
......
.form-group.project-visibility-level-holder
= f.label :visibility_level, class: 'control-label' do
Visibility Level
= link_to "(?)", help_page_path("public_access", "public_access")
.col-sm-10
- if can_change_visibility_level
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
= f.radio_button :visibility_level, level, disabled: restricted
= label "#{dom_class(@snippet)}_visibility_level", level do
= visibility_level_icon(level)
.option-title
= visibility_level_label(level)
.option-descr
= snippet_visibility_level_description(level)
- unless restricted_visibility_levels.empty?
.col-sm-10
%span.info
Some visibility level settings have been restricted by the administrator.
- else
.col-sm-10
%span.info
= visibility_level_icon(visibility_level)
%strong
= visibility_level_label(visibility_level)
.light= visibility_level_description(visibility_level)
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
Internal Internal
%span.pull-right %span.pull-right
= @user.snippets.are_internal.count = @user.snippets.are_internal.count
= nav_tab :scope, 'are_public' do
= link_to user_snippets_path(@user, scope: 'are_public') do
Public
%span.pull-right
= @user.snippets.are_public.count
.col-md-9.my-snippets .col-md-9.my-snippets
= render 'snippets' = render 'snippets'
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
Public snippets Public snippets
.pull-right .pull-right
- if current_user
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do = link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet Add new snippet
= link_to user_snippets_path(current_user), class: "btn btn-grouped" do = link_to user_snippets_path(current_user), class: "btn btn-grouped" do
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
%span %span
\/ \/
Snippets Snippets
- if current_user
= link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
Add new snippet Add new snippet
......
...@@ -13,7 +13,6 @@ module Gitlab ...@@ -13,7 +13,6 @@ module Gitlab
# Custom directories with classes and modules you want to be autoloadable. # Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib config.autoload_paths += %W(#{config.root}/lib
#{config.root}/app/finders
#{config.root}/app/models/hooks #{config.root}/app/models/hooks
#{config.root}/app/models/concerns #{config.root}/app/models/concerns
#{config.root}/app/models/project_services #{config.root}/app/models/project_services
...@@ -25,6 +24,7 @@ module Gitlab ...@@ -25,6 +24,7 @@ module Gitlab
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# NOTE: Please prefer set time zone on config/gitlab.yml configuration file.
# config.time_zone = 'Central Time (US & Canada)' # config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
......
...@@ -33,6 +33,11 @@ production: &base ...@@ -33,6 +33,11 @@ production: &base
# Uncomment and customize if you can't use the default user to run GitLab (default: 'git') # Uncomment and customize if you can't use the default user to run GitLab (default: 'git')
# user: git # user: git
## Date & Time settings
# Uncomment and customize if you want to change the default time zone of GitLab application.
# To see all available zones, run `bundle exec rake time:zones:all`
# time_zone: 'UTC'
## Email settings ## Email settings
# Email address used in the "From" field in mails sent by GitLab # Email address used in the "From" field in mails sent by GitLab
email_from: example@example.com email_from: example@example.com
...@@ -119,6 +124,7 @@ production: &base ...@@ -119,6 +124,7 @@ production: &base
# new_issue_url: "http://jira.sample/secure/CreateIssue.jspa" # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa"
## Gravatar ## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
gravatar: gravatar:
enabled: true # Use user avatar image from Gravatar.com (default: true) enabled: true # Use user avatar image from Gravatar.com (default: true)
# gravatar urls: possible placeholders: %{hash} %{size} %{email} # gravatar urls: possible placeholders: %{hash} %{size} %{email}
...@@ -134,6 +140,16 @@ production: &base ...@@ -134,6 +140,16 @@ production: &base
# bundle exec rake gitlab:ldap:check RAILS_ENV=production # bundle exec rake gitlab:ldap:check RAILS_ENV=production
ldap: ldap:
enabled: false enabled: false
servers:
main: # 'main' is the GitLab 'provider ID' of this LDAP server
## label
#
# A human-friendly name for your LDAP server. It is OK to change the label later,
# for instance if you find out it is too large to fit on the web page.
#
# Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP'
host: '_your_ldap_server' host: '_your_ldap_server'
port: 636 port: 636
uid: 'sAMAccountName' uid: 'sAMAccountName'
...@@ -172,6 +188,14 @@ production: &base ...@@ -172,6 +188,14 @@ production: &base
# #
user_filter: '' user_filter: ''
# GitLab EE only: add more LDAP servers
# Choose an ID made of a-z and 0-9 . This ID will be stored in the database
# so that GitLab can remember which LDAP server a user belongs to.
# uswest2:
# label:
# host:
# ....
## OmniAuth settings ## OmniAuth settings
omniauth: omniauth:
...@@ -299,6 +323,20 @@ test: ...@@ -299,6 +323,20 @@ test:
project_url: "http://redmine/projects/:issues_tracker_id" project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
ldap:
enabled: false
servers:
main:
label: ldap
host: 127.0.0.1
port: 3890
uid: 'uid'
method: 'plain' # "tls" or "ssl" or "plain"
base: 'dc=example,dc=com'
user_filter: ''
group_base: 'ou=groups,dc=example,dc=com'
admin_group: ''
sync_ssh_keys: false
staging: staging:
<<: *base <<: *base
...@@ -56,9 +56,25 @@ end ...@@ -56,9 +56,25 @@ end
# Default settings # Default settings
Settings['ldap'] ||= Settingslogic.new({}) Settings['ldap'] ||= Settingslogic.new({})
Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil?
Settings.ldap['allow_username_or_email_login'] = false if Settings.ldap['allow_username_or_email_login'].nil?
Settings.ldap['active_directory'] = true if Settings.ldap['active_directory'].nil?
# backwards compatibility, we only have one host
if Settings.ldap['enabled'] || Rails.env.test?
if Settings.ldap['host'].present?
server = Settings.ldap.except('sync_time')
server['provider_name'] = 'ldap'
Settings.ldap['servers'] = {
'ldap' => server
}
end
Settings.ldap['servers'].each do |key, server|
server['label'] ||= 'LDAP'
server['allow_username_or_email_login'] = false if server['allow_username_or_email_login'].nil?
server['active_directory'] = true if server['active_directory'].nil?
server['provider_name'] ||= "ldap#{key}".downcase
server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name'])
end
end
Settings['omniauth'] ||= Settingslogic.new({}) Settings['omniauth'] ||= Settingslogic.new({})
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
...@@ -87,6 +103,7 @@ Settings.gitlab['user_home'] ||= begin ...@@ -87,6 +103,7 @@ Settings.gitlab['user_home'] ||= begin
rescue ArgumentError # no user configured rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user'] '/home/' + Settings.gitlab['user']
end end
Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= false Settings.gitlab['signup_enabled'] ||= false
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil? Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
......
if Gitlab::LDAP::Config.enabled?
module OmniAuth::Strategies
server = Gitlab.config.ldap.servers.values.first
klass = server['provider_class']
const_set(klass, Class.new(LDAP)) unless klass == 'LDAP'
end
OmniauthCallbacksController.class_eval do
server = Gitlab.config.ldap.servers.values.first
alias_method server['provider_name'], :ldap
end
end
\ No newline at end of file
...@@ -204,23 +204,25 @@ Devise.setup do |config| ...@@ -204,23 +204,25 @@ Devise.setup do |config|
# manager.default_strategies(scope: :user).unshift :some_external_strategy # manager.default_strategies(scope: :user).unshift :some_external_strategy
# end # end
if Gitlab.config.ldap.enabled if Gitlab::LDAP::Config.enabled?
if Gitlab.config.ldap.allow_username_or_email_login Gitlab.config.ldap.servers.values.each do |server|
if server['allow_username_or_email_login']
email_stripping_proc = ->(name) {name.gsub(/@.*$/,'')} email_stripping_proc = ->(name) {name.gsub(/@.*$/,'')}
else else
email_stripping_proc = ->(name) {name} email_stripping_proc = ->(name) {name}
end end
config.omniauth :ldap, config.omniauth server['provider_name'],
host: Gitlab.config.ldap['host'], host: server['host'],
base: Gitlab.config.ldap['base'], base: server['base'],
uid: Gitlab.config.ldap['uid'], uid: server['uid'],
port: Gitlab.config.ldap['port'], port: server['port'],
method: Gitlab.config.ldap['method'], method: server['method'],
bind_dn: Gitlab.config.ldap['bind_dn'], bind_dn: server['bind_dn'],
password: Gitlab.config.ldap['password'], password: server['password'],
name_proc: email_stripping_proc name_proc: email_stripping_proc
end end
end
Gitlab.config.omniauth.providers.each do |provider| Gitlab.config.omniauth.providers.each do |provider|
provider_arguments = [] provider_arguments = []
......
# Be sure to restart your server when you modify this file.
require 'securerandom'
# Your secret key for verifying the gitlab_shell.
secret_file = Rails.root.join('.gitlab_shell_secret')
gitlab_shell_symlink = File.join(Gitlab.config.gitlab_shell.path, '.gitlab_shell_secret')
unless File.exist? secret_file
# Generate a new token of 16 random hexadecimal characters and store it in secret_file.
token = SecureRandom.hex(16)
File.write(secret_file, token)
end
if File.exist?(Gitlab.config.gitlab_shell.path) && !File.exist?(gitlab_shell_symlink)
FileUtils.symlink(secret_file, gitlab_shell_symlink)
end
\ No newline at end of file
Time.zone = Gitlab.config.gitlab.time_zone || Time.zone
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
contents = [ content =<<eos
`curl https://gist.githubusercontent.com/randx/4275756/raw/da2f262920c96d1a970d48bf2e99147954b1f4bd/glus1204.sh`, class Member < ActiveRecord::Base
`curl https://gist.githubusercontent.com/randx/3754594/raw/11026a295e6ef3a151c635707a3e1e8e15fc4725/gitlab_setup.sh`, include Notifiable
`curl https://gist.githubusercontent.com/randx/3065552/raw/29fbd09f4605a5ea22a5a9095e35fd1938dea4d6/gistfile1.sh`, include Gitlab::Access
]
belongs_to :user
belongs_to :source, polymorphic: true
validates :user, presence: true
validates :source, presence: true
validates :user_id, uniqueness: { scope: [:source_type, :source_id], message: "already exists in source" }
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) }
scope :developers, -> { where(access_level: DEVELOPER) }
scope :masters, -> { where(access_level: MASTER) }
scope :owners, -> { where(access_level: OWNER) }
delegate :name, :username, :email, to: :user, prefix: true
end
eos
(1..50).each do |i| (1..50).each do |i|
user = User.all.sample user = User.all.sample
...@@ -12,10 +29,11 @@ Gitlab::Seeder.quiet do ...@@ -12,10 +29,11 @@ Gitlab::Seeder.quiet do
id: i, id: i,
author_id: user.id, author_id: user.id,
title: Faker::Lorem.sentence(3), title: Faker::Lorem.sentence(3),
file_name: Faker::Internet.domain_word + '.sh', file_name: Faker::Internet.domain_word + '.rb',
private: [true, false].sample, visibility_level: Gitlab::VisibilityLevel.values.sample,
content: contents.sample, content: content,
}]) }])
print('.') print('.')
end end
end end
......
class AddVisibilityLevelToSnippet < ActiveRecord::Migration
def up
add_column :snippets, :visibility_level, :integer, :default => 0, :null => false
Snippet.where(private: true).update_all(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
Snippet.where(private: false).update_all(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
add_index :snippets, :visibility_level
remove_column :snippets, :private
end
def down
add_column :snippets, :private, :boolean, :default => false, :null => false
Snippet.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).update_all(private: false)
Snippet.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).update_all(private: true)
remove_column :snippets, :visibility_level
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20141006143943) do ActiveRecord::Schema.define(version: 20141007100818) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -299,14 +299,15 @@ ActiveRecord::Schema.define(version: 20141006143943) do ...@@ -299,14 +299,15 @@ ActiveRecord::Schema.define(version: 20141006143943) do
t.datetime "updated_at" t.datetime "updated_at"
t.string "file_name" t.string "file_name"
t.datetime "expires_at" t.datetime "expires_at"
t.boolean "private", default: true, null: false
t.string "type" t.string "type"
t.integer "visibility_level", default: 0, null: false
end end
add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
create_table "taggings", force: true do |t| create_table "taggings", force: true do |t|
t.integer "tag_id" t.integer "tag_id"
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
- [Update](update/README.md) Update guides to upgrade your installation. - [Update](update/README.md) Update guides to upgrade your installation.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
## Contributor documentation ## Contributor documentation
......
# Services
## GitLab CI
### Edit GitLab CI service
Set GitLab CI service for a project.
```
PUT /projects/:id/services/gitlab-ci
```
Parameters:
- `token` (required) - CI project token
- `project_url` (required) - CI project url
### Delete GitLab CI service
Delete GitLab CI service settings for a project.
```
DELETE /projects/:id/services/gitlab-ci
```
## Hipchat
### Edit Hipchat service
Set Hipchat service for project.
```
PUT /projects/:id/services/hipchat
```
Parameters:
- `token` (required) - Hipchat token
- `room` (required) - Hipchat room name
### Delete Hipchat service
Delete Hipchat service for a project.
```
DELETE /projects/:id/services/hipchat
```
# Use Libravatar service with GitLab
GitLab by default supports [Gravatar](gravatar.com) avatar service.
Libravatar is a service which delivers your avatar (profile picture) to other websites and their API is
[heavily based on gravatar](http://wiki.libravatar.org/api/).
This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server.
# Configuration
In [gitlab.yml gravatar section](https://gitlab.com/gitlab-org/gitlab-ce/blob/672bd3902d86b78d730cea809fce312ec49d39d7/config/gitlab.yml.example#L122) set
the configuration options as follows:
## For HTTP
```yml
gravatar:
enabled: true
# gravatar urls: possible placeholders: %{hash} %{size} %{email}
plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
## For HTTPS
```yml
gravatar:
enabled: true
# gravatar urls: possible placeholders: %{hash} %{size} %{email}
ssl_url: "https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
## Self-hosted
If you are [running your own libravatar service](http://wiki.libravatar.org/running_your_own/) the url will be different in the configuration
but the important part is to provide the same placeholders so GitLab can parse the url correctly.
For example, you host a service on `http://libravatar.example.com` the `plain_url` you need to supply in `gitlab.yml` is
`http://libravatar.example.com/avatar/%{hash}?s=%{size}&d=identicon`
## Omnibus-gitlab example
In `/etc/gitlab/gitlab.rb`:
#### For http
```ruby
gitlab_rails['gravatar_enabled'] = true
gitlab_rails['gravatar_plain_url'] = "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
#### For https
```ruby
gitlab_rails['gravatar_enabled'] = true
gitlab_rails['gravatar_ssl_url'] = "https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
Run `sudo gitlab-ctl reconfigure` for changes to take effect.
## Default URL for missing images
[Libravatar supports different sets](http://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
In order to use a different set other than `identicon`, replace `&d=identicon` portion of the url with another supported set.
For example, you can use `retro` set in which case url would look like: `plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=retro"`
...@@ -4,14 +4,14 @@ This document describes what services we use for testing GitLab and GitLab CI. ...@@ -4,14 +4,14 @@ This document describes what services we use for testing GitLab and GitLab CI.
We currently use three CI services to test GitLab: We currently use three CI services to test GitLab:
1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/2/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce) 1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/4/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce)
2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org 2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org
3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq) 3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
| Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore | | Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore |
|---------------------------------------|---------------------------|------------------------|-----------| |---------------------------------------|---------------------------|---------------------------------------------------------------------------|-----------|
| GitLab CE @ MySQL | ✓ | ✓ | | | GitLab CE @ MySQL | ✓ | ✓ [Core team can trigger builds](https://gitlab-ce.githost.io/projects/4) | |
| GitLab CE @ PostgreSQL | | | ✓ | | GitLab CE @ PostgreSQL | | | ✓ [Core team can trigger builds](https://semaphoreapp.com/gitlabhq/gitlabhq/branches/master) |
| GitLab EE @ MySQL | ✓ | | | | GitLab EE @ MySQL | ✓ | | |
| GitLab CI @ MySQL | ✓ | | | | GitLab CI @ MySQL | ✓ | | |
| GitLab CI @ PostgreSQL | | | ✓ | | GitLab CI @ PostgreSQL | | | ✓ |
...@@ -19,13 +19,15 @@ We currently use three CI services to test GitLab: ...@@ -19,13 +19,15 @@ We currently use three CI services to test GitLab:
| GitLab Shell | ✓ | | ✓ | | GitLab Shell | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ | | GitLab Shell | ✓ | | ✓ |
Core team has access to trigger builds if needed for GitLab CE.
We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/build_script_gitlab_ce.md) for testing with GitLab CI. We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/build_script_gitlab_ce.md) for testing with GitLab CI.
# Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq) # Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
Language: Ruby - Language: Ruby
Ruby verion: 2.1.2 - Ruby verion: 2.1.2
database.yml: pg - database.yml: pg
Build commands Build commands
......
# Installation # Installation
## Consider the Omnibus package installation
Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm).
## Select Version to Install ## Select Version to Install
Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install. In most cases this should be the highest numbered stable branch (example shown below). Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install. In most cases this should be the highest numbered stable branch (example shown below).
...@@ -70,8 +74,8 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -70,8 +74,8 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source # Download and compile from source
cd /tmp cd /tmp
curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.0.0.tar.gz | tar xz curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.1.2.tar.gz | tar xz
cd git-2.0.0/ cd git-2.1.2/
make prefix=/usr/local all make prefix=/usr/local all
# Install into /usr/local/bin # Install into /usr/local/bin
...@@ -161,9 +165,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -161,9 +165,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
### Clone the Source ### Clone the Source
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-3-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-4-stable gitlab
**Note:** You can change `7-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! **Note:** You can change `7-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It ### Configure It
......
...@@ -6,6 +6,95 @@ The first time a user signs in with LDAP credentials, GitLab will create a new G ...@@ -6,6 +6,95 @@ The first time a user signs in with LDAP credentials, GitLab will create a new G
GitLab user attributes such as nickname and email will be copied from the LDAP user entry. GitLab user attributes such as nickname and email will be copied from the LDAP user entry.
## Configuring GitLab for LDAP integration
To enable GitLab LDAP integration you need to add your LDAP server settings in `/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml`.
In GitLab Enterprise Edition you can have multiple LDAP servers connected to one GitLab server.
Please note that before version 7.4, GitLab used a different syntax for configuring LDAP integration.
The old LDAP integration syntax still works in GitLab 7.4.
If your `gitlab.rb` or `gitlab.yml` file contains LDAP settings in both the old syntax and the new syntax, only the __old__ syntax will be used by GitLab.
```ruby
# For omnibus packages
gitlab_rails['ldap_enabled'] = true
gitlab_rails['ldap_servers'] = YAML.load <<-EOS # remember to close this block with 'EOS' below
main: # 'main' is the GitLab 'provider ID' of this LDAP server
## label
#
# A human-friendly name for your LDAP server. It is OK to change the label later,
# for instance if you find out it is too large to fit on the web page.
#
# Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP'
host: '_your_ldap_server'
port: 636
uid: 'sAMAccountName'
method: 'ssl' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
# This setting specifies if LDAP server is Active Directory LDAP server.
# For non AD servers it skips the AD specific queries.
# If your LDAP server is not AD, set this to false.
active_directory: true
# If allow_username_or_email_login is enabled, GitLab will ignore everything
# after the first '@' in the LDAP username submitted by the user on login.
#
# Example:
# - the user enters 'jane.doe@example.com' and 'p@ssw0rd' as LDAP credentials;
# - GitLab queries the LDAP server with 'jane.doe' and 'p@ssw0rd'.
#
# If you are using "uid: 'userPrincipalName'" on ActiveDirectory you need to
# disable this setting, because the userPrincipalName contains an '@'.
allow_username_or_email_login: false
# Base where we can search for users
#
# Ex. ou=People,dc=gitlab,dc=example
#
base: ''
# Filter LDAP users
#
# Format: RFC 4515 http://tools.ietf.org/search/rfc4515
# Ex. (employeeType=developer)
#
# Note: GitLab does not support omniauth-ldap's custom filter syntax.
#
user_filter: ''
# GitLab EE only: add more LDAP servers
# Choose an ID made of a-z and 0-9 . This ID will be stored in the database
# so that GitLab can remember which LDAP server a user belongs to.
# uswest2:
# label:
# host:
# ....
EOS
```
If you are using a GitLab installation from source you can find the LDAP settings in `/home/git/gitlab/config/gitlab.yml`:
```
production:
# snip...
ldap:
enabled: false
servers:
main: # 'main' is the GitLab 'provider ID' of this LDAP server
## label
#
# A human-friendly name for your LDAP server. It is OK to change the label later,
# for instance if you find out it is too large to fit on the web page.
#
# Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP'
# snip...
```
## Enabling LDAP sign-in for existing GitLab users ## Enabling LDAP sign-in for existing GitLab users
When a user signs in to GitLab with LDAP for the first time, and their LDAP email address is the primary email address of an existing GitLab user, then the LDAP DN will be associated with the existing user. When a user signs in to GitLab with LDAP for the first time, and their LDAP email address is the primary email address of an existing GitLab user, then the LDAP DN will be associated with the existing user.
...@@ -24,14 +113,21 @@ If you want to limit all GitLab access to a subset of the LDAP users on your LDA ...@@ -24,14 +113,21 @@ If you want to limit all GitLab access to a subset of the LDAP users on your LDA
The filter must comply with [RFC 4515](http://tools.ietf.org/search/rfc4515). The filter must comply with [RFC 4515](http://tools.ietf.org/search/rfc4515).
```ruby ```ruby
# For omnibus-gitlab # For omnibus packages; new LDAP server syntax
gitlab_rails['ldap_user_filter'] = '(employeeType=developer)' gitlab_rails['ldap_servers'] = YAML.load <<-EOS
main:
# snip...
user_filter: '(employeeType=developer)'
EOS
``` ```
```yaml ```yaml
# For installations from source # For installations from source; new LDAP server syntax
production: production:
ldap: ldap:
servers:
main:
# snip...
user_filter: '(employeeType=developer)' user_filter: '(employeeType=developer)'
``` ```
......
...@@ -510,6 +510,10 @@ Code above produces next output: ...@@ -510,6 +510,10 @@ Code above produces next output:
| cell 1 | cell 2 | | cell 1 | cell 2 |
| cell 3 | cell 4 | | cell 3 | cell 4 |
**Note**
The row of dashes between the table header and body must have at least three dashes in each column.
## References ## References
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
......
...@@ -191,6 +191,7 @@ It is important to do this as soon as possible, so we can catch any errors befor ...@@ -191,6 +191,7 @@ It is important to do this as soon as possible, so we can catch any errors befor
- Ask Dmitriy to add screenshots to the WIP MR. - Ask Dmitriy to add screenshots to the WIP MR.
- Decide with team who will be the MVP user. - Decide with team who will be the MVP user.
- Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. - Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
- Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master)
- Assign to one reviewer who will fix spelling issues by editing the branch (can use the online editor) - Assign to one reviewer who will fix spelling issues by editing the branch (can use the online editor)
- After the reviewer is finished the whole team will be mentioned to give their suggestions via line comments - After the reviewer is finished the whole team will be mentioned to give their suggestions via line comments
......
...@@ -195,6 +195,12 @@ sudo rm -R tmp ...@@ -195,6 +195,12 @@ sudo rm -R tmp
sudo -u git -H mkdir tmp sudo -u git -H mkdir tmp
sudo chmod -R u+rwX tmp/ sudo chmod -R u+rwX tmp/
# create directory for pids, make sure GitLab can write to it
sudo -u git -H mkdir tmp/pids/
sudo chmod -R u+rwX tmp/pids/
# if you are already running a newer version of GitLab check that installation guide for other tmp folders you need to create
# reboot system # reboot system
sudo reboot sudo reboot
......
# From 6.x or 7.x to 7.3 # 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.3. 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 ## Global issue numbers
...@@ -64,13 +64,13 @@ sudo gem install bundler --no-ri --no-rdoc ...@@ -64,13 +64,13 @@ sudo gem install bundler --no-ri --no-rdoc
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H git fetch --all 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: For GitLab Community Edition:
```bash ```bash
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically sudo -u git -H git checkout 7-4-stable
sudo -u git -H git checkout 7-3-stable
``` ```
OR OR
...@@ -78,8 +78,7 @@ OR ...@@ -78,8 +78,7 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically sudo -u git -H git checkout 7-4-stable-ee
sudo -u git -H git checkout 7-3-stable-ee
``` ```
## 4. Install additional packages ## 4. Install additional packages
...@@ -153,14 +152,14 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ...@@ -153,14 +152,14 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
TIP: to see what changed in `gitlab.yml.example` in this release use next command: 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 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-3-stable/config/gitlab.yml.example but with your settings. * 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-3-stable/config/unicorn.rb.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.0/config.yml.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/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab but with your settings. * HTTP setups: Make `/etc/nginx/sites-available/nginx` 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/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab-ssl but with your settings. * HTTPS setups: Make `/etc/nginx/sites-available/nginx-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 * Copy rack attack middleware config
```bash ```bash
......
...@@ -18,12 +18,12 @@ sudo service gitlab stop ...@@ -18,12 +18,12 @@ sudo service gitlab stop
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H git fetch --all 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: For GitLab Community Edition:
```bash ```bash
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout 7-3-stable sudo -u git -H git checkout 7-3-stable
``` ```
...@@ -32,7 +32,6 @@ OR ...@@ -32,7 +32,6 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout 7-3-stable-ee sudo -u git -H git checkout 7-3-stable-ee
``` ```
...@@ -75,7 +74,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ...@@ -75,7 +74,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
# Enable Redis socket for default Debian / Ubuntu path # Enable Redis socket for default Debian / Ubuntu path
echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf 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). # Be sure redis group can write to the socket, enable only if supported (>= redis 2.4.0).
sed -i '/# unixsocketperm/ s/^# unixsocketperm.*/unixsocketperm 0775/' /etc/redis/redis.conf sudo sed -i '/# unixsocketperm/ s/^# unixsocketperm.*/unixsocketperm 0775/' /etc/redis/redis.conf
# Activate the changes to redis.conf # Activate the changes to redis.conf
sudo service redis-server restart sudo service redis-server restart
# Add git to the redis group # Add git to the redis group
......
# From 7.3 to 7.4
### 0. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. 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
```
### 3. 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
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
### 4. 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).
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
### 5. Update config files
#### New configuration options for gitlab.yml
There are new configuration options available for gitlab.yml. View them with the command below and apply them to your current gitlab.yml.
```
git diff origin/7-3-stable:config/gitlab.yml.example origin/7-4-stable:config/gitlab.yml.example
```
#### Change timeout for unicorn
```
# config/unicorn.rb
timeout 60
```
#### Change nginx https settings
* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/lib/support/nginx/gitlab-ssl but with your setting
#### Update database.yml config file(for mysql only) if needed (basically it is required for old gitlab installations)
* Add `collation: utf8_general_ci` to config/database.yml as seen in [config/database.yml.mysql](config/database.yml.mysql)
### 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 is complete!
### 8. 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](../integration/google.md).
### 9. 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.
```
# 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 (7.3)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 7.2 to 7.3](7.2-to-7.3.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
```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
...@@ -4,3 +4,5 @@ ...@@ -4,3 +4,5 @@
- [Groups](groups.md) - [Groups](groups.md)
- [Labels](labels.md) - [Labels](labels.md)
- [GitLab Flow](gitlab_flow.md) - [GitLab Flow](gitlab_flow.md)
- [Notifications](notifications.md)
- [Migrating from SVN to GitLab](migrating_from_svn.md)
...@@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng ...@@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng
Since many organizations new to git have no conventions how to work with it, it can quickly become a mess. Since many organizations new to git have no conventions how to work with it, it can quickly become a mess.
The biggest problem they run into is that many long running branches that each contain part of the changes are around. The biggest problem they run into is that many long running branches that each contain part of the changes are around.
People have a hard time figuring out which branch they should develop on or deploy to production. People have a hard time figuring out which branch they should develop on or deploy to production.
Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](https://guides.github.com/introduction/flow/index.html) Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html)
We think there is still room for improvement and will detail a set of practices we call GitLab flow. We think there is still room for improvement and will detail a set of practices we call GitLab flow.
# Git flow and its problems # Git flow and its problems
......
# Migrating from SVN to GitLab
SVN stands for Subversion and is a version control system (VCS).
Git is a distributed version control system.
There are some major differences between the two, for more information consult your favourite search engine.
Git has tools for migrating SVN repositories to git, namely `git svn`. You can read more about this at
[git documentation pages](http://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
Apart from the [official git documentation](http://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
user created step by step guide for migrating from SVN to GitLab.
[Benjamin New](https://github.com/leftclickben) wrote [a guide that shows how to do a migration](https://gist.github.com/leftclickben/322b7a3042cbe97ed2af). Mirrors can be found [here](https://gitlab.com/snippets/2168) and [here](https://gist.github.com/maxlazio/f1b593b0d00aa966e9ca).
## Contribute to this guide
We welcome all contributions that would expand this guide with instructions on how to migrate from SVN and other version control systems.
# GitLab Notifications
GitLab has notifications system in place to notify a user of events important for the workflow.
## Notification settings
Under user profile page you can find the notification settings.
![notification settings](notifications/settings.png)
Notification settings are divided into three groups:
* Global Settings
* Group Settings
* Project Settings
Each of these settings have levels of notification:
* Disabled - turns off notifications
* Participating - receive notifications from related resources
* Watch - receive notifications from projects or groups user is a member of
* Global - notifications as set at the global settings
#### Global Settings
Global Settings are at the bottom of the hierarchy.
Any setting set here will be overriden by a setting at the group or a project level.
Group or Project settings can use `global` notification setting which will then use
anything that is set at Global Settings.
#### Group Settings
Group Settings are taking presedence over Global Settings but are on a level below Project Settings.
This means that you can set a different level of notifications per group while still being able
to have a finer level setting per project.
Organization like this is suitable for users that belong to different groups but don't have the
same need for being notified for every group they are member of.
#### Project Settings
Project Settings are at the top level and any setting placed at this level will take presedence of any
other setting.
This is suitable for users that have different needs for notifications per project basis.
## Notification events
Below is the table of events users can be notified of:
| Event | Sent to | Settings level |
|------------------------------|-------------------------------------------------------------------|------------------------------|
| New SSH key added | User | Security email, always sent. |
| New email added | User | Security email, always sent. |
| New user created | User | Sent on user creation, except for omniauth (LDAP)|
| New issue created | Issue assignee [1], project members [2] | [1] not disabled, [2] higher than participating |
| User added to project | User | Sent when user is added to project |
| Project access level changed | User | Sent when user project access level is changed |
| User added to group | User | Sent when user is added to group |
| Project moved | Project members [1] | [1] not disabled |
| Group access level changed | User | Sent when user group access level is changed |
| Close issue | Issue author [1], issue assignee [2], project members [3] | [1] [2] not disabled, [3] higher than participating |
| Reassign issue | New issue assignee [1], old issue assignee [2] | [1] [2] not disabled |
| Reopen issue | Project members [1] | [1] higher than participating |
| New merge request | MR assignee [1] | [1] not disabled |
| Reassign merge request | New MR assignee [1], old MR assignee [2] | [1] [2] not disabled |
| Close merge request | MR author [1], MR assignee [2], project members [3] | [1] [2] not disabled, [3] higher than participating |
| Reopen merge request | Project members [1] | [1] higher than participating |
| Merge merge request | MR author [1], MR assignee [2], project members [3] | [1] [2] not disabled, [3] higher than participating |
| New comment | Mentioned users [1], users participating [2], project members [3] | [1] [2] not disabled, [3] higher than participating |
...@@ -20,3 +20,10 @@ Feature: Admin Groups ...@@ -20,3 +20,10 @@ Feature: Admin Groups
When I visit admin group page When I visit admin group page
When I select user "John Doe" from user list as "Reporter" When I select user "John Doe" from user list as "Reporter"
Then I should see "John Doe" in team list in every project as "Reporter" Then I should see "John Doe" in team list in every project as "Reporter"
@javascript
Scenario: Remove user from group
Given we have user "John Doe" in group
When I visit admin group page
And I remove user "John Doe" from group
Then I should not see "John Doe" in team list
...@@ -30,10 +30,20 @@ Feature: Project Source Browse Files ...@@ -30,10 +30,20 @@ Feature: Project Source Browse Files
And I edit code And I edit code
And I fill the new file name And I fill the new file name
And I fill the commit message And I fill the commit message
And I click on "Commit changes" And I click on "Commit Changes"
Then I am redirected to the new file Then I am redirected to the new file
And I should see its new content And I should see its new content
@javascript
Scenario: If I enter an illegal file name I see an error message
Given I click on "new file" link in repo
And I fill the new file name with an illegal name
And I edit code
And I fill the commit message
And I click on "Commit changes"
Then I am on the new file page
And I see a commit error message
@javascript @javascript
Scenario: I can edit file Scenario: I can edit file
Given I click on ".gitignore" file in repo Given I click on ".gitignore" file in repo
...@@ -46,10 +56,20 @@ Feature: Project Source Browse Files ...@@ -46,10 +56,20 @@ Feature: Project Source Browse Files
And I click button "Edit" And I click button "Edit"
And I edit code And I edit code
And I fill the commit message And I fill the commit message
And I click on "Commit changes" And I click on "Commit Changes"
Then I am redirected to the ".gitignore" Then I am redirected to the ".gitignore"
And I should see its new content And I should see its new content
@javascript @wip
Scenario: If I don't change the content of the file I see an error message
Given I click on ".gitignore" file in repo
And I click button "edit"
And I fill the commit message
And I click on "Commit changes"
# Test fails because carriage returns are added to the file.
Then I am on the ".gitignore" edit file page
And I see a commit error message
@javascript @javascript
Scenario: I can see editing preview Scenario: I can see editing preview
Given I click on ".gitignore" file in repo Given I click on ".gitignore" file in repo
......
...@@ -4,8 +4,10 @@ Feature: Snippets Discover ...@@ -4,8 +4,10 @@ Feature: Snippets Discover
Given I sign in as a user Given I sign in as a user
And I have public "Personal snippet one" snippet And I have public "Personal snippet one" snippet
And I have private "Personal snippet private" snippet And I have private "Personal snippet private" snippet
And I have internal "Personal snippet internal" snippet
Scenario: I should see snippets Scenario: I should see snippets
Given I visit snippets page Given I visit snippets page
Then I should see "Personal snippet one" in snippets Then I should see "Personal snippet one" in snippets
And I should see "Personal snippet internal" in snippets
And I should not see "Personal snippet private" in snippets And I should not see "Personal snippet private" in snippets
...@@ -4,20 +4,31 @@ Feature: Snippets User ...@@ -4,20 +4,31 @@ Feature: Snippets User
Given I sign in as a user Given I sign in as a user
And I have public "Personal snippet one" snippet And I have public "Personal snippet one" snippet
And I have private "Personal snippet private" snippet And I have private "Personal snippet private" snippet
And I have internal "Personal snippet internal" snippet
Scenario: I should see all my snippets Scenario: I should see all my snippets
Given I visit my snippets page Given I visit my snippets page
Then I should see "Personal snippet one" in snippets Then I should see "Personal snippet one" in snippets
And I should see "Personal snippet private" in snippets And I should see "Personal snippet private" in snippets
And I should see "Personal snippet internal" in snippets
Scenario: I can see only my private snippets Scenario: I can see only my private snippets
Given I visit my snippets page Given I visit my snippets page
And I click "Private" filter And I click "Private" filter
Then I should not see "Personal snippet one" in snippets Then I should not see "Personal snippet one" in snippets
And I should not see "Personal snippet internal" in snippets
And I should see "Personal snippet private" in snippets And I should see "Personal snippet private" in snippets
Scenario: I can see only my public snippets Scenario: I can see only my public snippets
Given I visit my snippets page Given I visit my snippets page
And I click "Internal" filter And I click "Public" filter
Then I should see "Personal snippet one" in snippets Then I should see "Personal snippet one" in snippets
And I should not see "Personal snippet private" in snippets And I should not see "Personal snippet private" in snippets
And I should not see "Personal snippet internal" in snippets
Scenario: I can see only my internal snippets
Given I visit my snippets page
And I click "Internal" filter
Then I should see "Personal snippet internal" in snippets
And I should not see "Personal snippet private" in snippets
And I should not see "Personal snippet one" in snippets
...@@ -37,8 +37,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps ...@@ -37,8 +37,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end end
When 'I select user "John Doe" from user list as "Reporter"' do When 'I select user "John Doe" from user list as "Reporter"' do
user = User.find_by(name: "John Doe") select2(user_john.id, from: "#user_ids", multiple: true)
select2(user.id, from: "#user_ids", multiple: true)
within "#new_team_member" do within "#new_team_member" do
select "Reporter", from: "access_level" select "Reporter", from: "access_level"
end end
...@@ -58,9 +57,29 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps ...@@ -58,9 +57,29 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end end
end end
step 'we have user "John Doe" in group' do
current_group.add_user(user_john, Gitlab::Access::REPORTER)
end
step 'I remove user "John Doe" from group' do
within "#user_#{user_john.id}" do
click_link 'Remove user from group'
end
end
step 'I should not see "John Doe" in team list' do
within ".group-users-list" do
page.should_not have_content "John Doe"
end
end
protected protected
def current_group def current_group
@group ||= Group.first @group ||= Group.first
end end
def user_john
@user_john ||= User.find_by(name: "John Doe")
end
end end
...@@ -8,7 +8,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -8,7 +8,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
commit = @project.repository.commit commit = @project.repository.commit
page.should have_content(@project.name) page.should have_content(@project.name)
page.should have_content(commit.message[0..20]) page.should have_content(commit.message[0..20])
page.should have_content(commit.id.to_s[0..5]) page.should have_content(commit.short_id)
end end
step 'I click atom feed link' do step 'I click atom feed link' do
......
...@@ -111,7 +111,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -111,7 +111,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click on the commit in the merge request' do step 'I click on the commit in the merge request' do
within '.mr-commits' do within '.mr-commits' do
click_link sample_commit.id[0..8] click_link Commit.truncate_sha(sample_commit.id)
end end
end end
......
...@@ -61,6 +61,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -61,6 +61,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
fill_in :file_name, with: new_file_name fill_in :file_name, with: new_file_name
end end
step 'I fill the new file name with an illegal name' do
fill_in :file_name, with: '.git'
end
step 'I fill the commit message' do step 'I fill the commit message' do
fill_in :commit_message, with: 'Not yet a commit message.' fill_in :commit_message, with: 'Not yet a commit message.'
end end
...@@ -69,8 +73,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -69,8 +73,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
click_link 'Diff' click_link 'Diff'
end end
step 'I click on "Commit changes"' do step 'I click on "Commit Changes"' do
click_button 'Commit changes' click_button 'Commit Changes'
end end
step 'I click on "Remove"' do step 'I click on "Remove"' do
...@@ -151,6 +155,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -151,6 +155,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).not_to have_link('permalink') expect(page).not_to have_link('permalink')
end end
step 'I see a commit error message' do
expect(page).to have_content('Your changes could not be committed')
end
private private
def set_new_content def set_new_content
......
...@@ -265,6 +265,15 @@ module SharedPaths ...@@ -265,6 +265,15 @@ module SharedPaths
visit project_blob_path(@project, File.join(root_ref, '.gitignore')) visit project_blob_path(@project, File.join(root_ref, '.gitignore'))
end end
step 'I am on the new file page' do
current_path.should eq(project_new_tree_path(@project, root_ref))
end
step 'I am on the ".gitignore" edit file page' do
current_path.should eq(project_edit_tree_path(
@project, File.join(root_ref, '.gitignore')))
end
step 'I visit project source page for "6d39438"' do step 'I visit project source page for "6d39438"' do
visit project_tree_path(@project, "6d39438") visit project_tree_path(@project, "6d39438")
end end
......
...@@ -6,7 +6,7 @@ module SharedSnippet ...@@ -6,7 +6,7 @@ module SharedSnippet
title: "Personal snippet one", title: "Personal snippet one",
content: "Test content", content: "Test content",
file_name: "snippet.rb", file_name: "snippet.rb",
private: false, visibility_level: Snippet::PUBLIC,
author: current_user) author: current_user)
end end
...@@ -15,9 +15,19 @@ module SharedSnippet ...@@ -15,9 +15,19 @@ module SharedSnippet
title: "Personal snippet private", title: "Personal snippet private",
content: "Provate content", content: "Provate content",
file_name: "private_snippet.rb", file_name: "private_snippet.rb",
private: true, visibility_level: Snippet::PRIVATE,
author: current_user) author: current_user)
end end
step 'I have internal "Personal snippet internal" snippet' do
create(:personal_snippet,
title: "Personal snippet internal",
content: "Provate content",
file_name: "internal_snippet.rb",
visibility_level: Snippet::INTERNAL,
author: current_user)
end
step 'I have a public many lined snippet' do step 'I have a public many lined snippet' do
create(:personal_snippet, create(:personal_snippet,
title: 'Many lined snippet', title: 'Many lined snippet',
...@@ -38,7 +48,7 @@ module SharedSnippet ...@@ -38,7 +48,7 @@ module SharedSnippet
|line fourteen |line fourteen
END END
file_name: 'many_lined_snippet.rb', file_name: 'many_lined_snippet.rb',
private: true, visibility_level: Snippet::PUBLIC,
author: current_user) author: current_user)
end end
end end
...@@ -7,6 +7,10 @@ class Spinach::Features::SnippetsDiscover < Spinach::FeatureSteps ...@@ -7,6 +7,10 @@ class Spinach::Features::SnippetsDiscover < Spinach::FeatureSteps
page.should have_content "Personal snippet one" page.should have_content "Personal snippet one"
end end
step 'I should see "Personal snippet internal" in snippets' do
page.should have_content "Personal snippet internal"
end
step 'I should not see "Personal snippet private" in snippets' do step 'I should not see "Personal snippet private" in snippets' do
page.should_not have_content "Personal snippet private" page.should_not have_content "Personal snippet private"
end end
......
...@@ -15,6 +15,10 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps ...@@ -15,6 +15,10 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
page.should have_content "Personal snippet private" page.should have_content "Personal snippet private"
end end
step 'I should see "Personal snippet internal" in snippets' do
page.should have_content "Personal snippet internal"
end
step 'I should not see "Personal snippet one" in snippets' do step 'I should not see "Personal snippet one" in snippets' do
page.should_not have_content "Personal snippet one" page.should_not have_content "Personal snippet one"
end end
...@@ -23,6 +27,10 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps ...@@ -23,6 +27,10 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
page.should_not have_content "Personal snippet private" page.should_not have_content "Personal snippet private"
end end
step 'I should not see "Personal snippet internal" in snippets' do
page.should_not have_content "Personal snippet internal"
end
step 'I click "Internal" filter' do step 'I click "Internal" filter' do
within('.nav-stacked') do within('.nav-stacked') do
click_link "Internal" click_link "Internal"
...@@ -35,6 +43,12 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps ...@@ -35,6 +43,12 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
end end
end end
step 'I click "Public" filter' do
within('.nav-stacked') do
click_link "Public"
end
end
def snippet def snippet
@snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one") @snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one")
end end
......
...@@ -85,7 +85,7 @@ module API ...@@ -85,7 +85,7 @@ module API
branch_name: branch_name branch_name: branch_name
} }
else else
render_api_error!(result[:error], 400) render_api_error!(result[:message], 400)
end end
end end
...@@ -117,7 +117,7 @@ module API ...@@ -117,7 +117,7 @@ module API
branch_name: branch_name branch_name: branch_name
} }
else else
render_api_error!(result[:error], 400) render_api_error!(result[:message], 400)
end end
end end
...@@ -149,7 +149,7 @@ module API ...@@ -149,7 +149,7 @@ module API
branch_name: branch_name branch_name: branch_name
} }
else else
render_api_error!(result[:error], 400) render_api_error!(result[:message], 400)
end end
end end
end end
......
...@@ -67,6 +67,10 @@ module API ...@@ -67,6 +67,10 @@ module API
unauthorized! unless current_user unauthorized! unless current_user
end end
def authenticate_by_gitlab_shell_token!
unauthorized! unless secret_token == params['secret_token']
end
def authenticated_as_admin! def authenticated_as_admin!
forbidden! unless current_user.is_admin? forbidden! unless current_user.is_admin?
end end
...@@ -193,5 +197,9 @@ module API ...@@ -193,5 +197,9 @@ module API
abilities abilities
end end
end end
def secret_token
File.read(Rails.root.join('.gitlab_shell_secret'))
end
end end
end end
module API module API
# Internal access API # Internal access API
class Internal < Grape::API class Internal < Grape::API
before {
authenticate_by_gitlab_shell_token!
}
namespace 'internal' do namespace 'internal' do
# Check if git command is allowed to project # Check if git command is allowed to project
# #
......
...@@ -28,7 +28,7 @@ module API ...@@ -28,7 +28,7 @@ module API
# Delete GitLab CI service settings # Delete GitLab CI service settings
# #
# Example Request: # Example Request:
# DELETE /projects/:id/keys/:id # DELETE /projects/:id/services/gitlab-ci
delete ":id/services/gitlab-ci" do delete ":id/services/gitlab-ci" do
if user_project.gitlab_ci_service if user_project.gitlab_ci_service
user_project.gitlab_ci_service.update_attributes( user_project.gitlab_ci_service.update_attributes(
...@@ -38,7 +38,41 @@ module API ...@@ -38,7 +38,41 @@ module API
) )
end end
end end
# Set Hipchat service for project
#
# Parameters:
# token (required) - Hipchat token
# room (required) - Hipchat room name
#
# Example Request:
# PUT /projects/:id/services/hipchat
put ':id/services/hipchat' do
required_attributes! [:token, :room]
attrs = attributes_for_keys [:token, :room]
user_project.build_missing_services
if user_project.hipchat_service.update_attributes(
attrs.merge(active: true))
true
else
not_found!
end end
end end
end
# Delete Hipchat service settings
#
# Example Request:
# DELETE /projects/:id/services/hipchat
delete ':id/services/hipchat' do
if user_project.hipchat_service
user_project.hipchat_service.update_attributes(
active: false,
token: nil,
room: nil
)
end
end
end
end
end
...@@ -30,7 +30,7 @@ module Backup ...@@ -30,7 +30,7 @@ module Backup
if File.exists?(path_to_repo(wiki)) if File.exists?(path_to_repo(wiki))
print " * #{wiki.path_with_namespace} ... " print " * #{wiki.path_with_namespace} ... "
if wiki.empty? if wiki.repository.empty?
puts " [SKIPPED]".cyan puts " [SKIPPED]".cyan
else else
output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)) output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all))
......
module Gitlab module Gitlab
class AppLogger < Gitlab::Logger class AppLogger < Gitlab::Logger
def self.file_name def self.file_name_noext
'application.log' 'application'
end end
def format_message(severity, timestamp, progname, msg) def format_message(severity, timestamp, progname, msg)
......
...@@ -3,22 +3,16 @@ module Gitlab ...@@ -3,22 +3,16 @@ module Gitlab
def find(login, password) def find(login, password)
user = User.find_by(email: login) || User.find_by(username: login) user = User.find_by(email: login) || User.find_by(username: login)
# If no user is found, or it's an LDAP server, try LDAP.
# LDAP users are only authenticated via LDAP
if user.nil? || user.ldap_user? if user.nil? || user.ldap_user?
# Second chance - try LDAP authentication # Second chance - try LDAP authentication
return nil unless ldap_conf.enabled return nil unless Gitlab::LDAP::Config.enabled?
Gitlab::LDAP::User.authenticate(login, password) Gitlab::LDAP::Authentication.login(login, password)
else else
user if user.valid_password?(password) user if user.valid_password?(password)
end end
end end
def log
Gitlab::AppLogger
end
def ldap_conf
@ldap_conf ||= Gitlab.config.ldap
end
end end
end end
...@@ -90,7 +90,7 @@ module Grack ...@@ -90,7 +90,7 @@ module Grack
when *Gitlab::GitAccess::PUSH_COMMANDS when *Gitlab::GitAccess::PUSH_COMMANDS
if user if user
# Skip user authorization on upload request. # Skip user authorization on upload request.
# It will be serverd by update hook in repository # It will be done by the pre-receive hook in the repository.
true true
else else
false false
......
module Gitlab module Gitlab
class GitLogger < Gitlab::Logger class GitLogger < Gitlab::Logger
def self.file_name def self.file_name_noext
'githost.log' 'githost'
end end
def format_message(severity, timestamp, progname, msg) def format_message(severity, timestamp, progname, msg)
......
# LDAP authorization model
#
# * Check if we are allowed access (not blocked)
#
module Gitlab module Gitlab
module LDAP module LDAP
class Access class Access
attr_reader :adapter attr_reader :adapter, :provider, :user
def self.open(&block) def self.open(user, &block)
Gitlab::LDAP::Adapter.open do |adapter| Gitlab::LDAP::Adapter.open(user.provider) do |adapter|
block.call(self.new(adapter)) block.call(self.new(user, adapter))
end end
end end
def self.allowed?(user) def self.allowed?(user)
self.open do |access| self.open(user) do |access|
if access.allowed?(user) if access.allowed?
# GitLab EE LDAP code goes here
user.last_credential_check_at = Time.now user.last_credential_check_at = Time.now
user.save user.save
true true
...@@ -22,21 +25,30 @@ module Gitlab ...@@ -22,21 +25,30 @@ module Gitlab
end end
end end
def initialize(adapter=nil) def initialize(user, adapter=nil)
@adapter = adapter @adapter = adapter
@user = user
@provider = user.provider
end end
def allowed?(user) def allowed?
if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
if Gitlab.config.ldap.active_directory return true unless ldap_config.active_directory
!Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter) !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter)
end
else else
false false
end end
rescue rescue
false false
end end
def adapter
@adapter ||= Gitlab::LDAP::Adapter.new(provider)
end
def ldap_config
Gitlab::LDAP::Config.new(provider)
end
end end
end end
end end
module Gitlab module Gitlab
module LDAP module LDAP
class Adapter class Adapter
attr_reader :ldap attr_reader :provider, :ldap
def self.open(&block) def self.open(provider, &block)
Net::LDAP.open(adapter_options) do |ldap| Net::LDAP.open(config(provider).adapter_options) do |ldap|
block.call(self.new(ldap)) block.call(self.new(provider, ldap))
end end
end end
def self.config def self.config(provider)
Gitlab.config.ldap Gitlab::LDAP::Config.new(provider)
end end
def self.adapter_options def initialize(provider, ldap=nil)
encryption = @provider = provider
case config['method'].to_s @ldap = ldap || Net::LDAP.new(config.adapter_options)
when 'ssl'
:simple_tls
when 'tls'
:start_tls
else
nil
end
options = {
host: config['host'],
port: config['port'],
encryption: encryption
}
auth_options = {
auth: {
method: :simple,
username: config['bind_dn'],
password: config['password']
}
}
if config['password'] || config['bind_dn']
options.merge!(auth_options)
end end
options
end
def initialize(ldap=nil) def config
@ldap = ldap || Net::LDAP.new(self.class.adapter_options) Gitlab::LDAP::Config.new(provider)
end end
def users(field, value) def users(field, value, limit = nil)
if field.to_sym == :dn if field.to_sym == :dn
options = { options = {
base: value, base: value,
...@@ -57,13 +30,13 @@ module Gitlab ...@@ -57,13 +30,13 @@ module Gitlab
} }
else else
options = { options = {
base: config['base'], base: config.base,
filter: Net::LDAP::Filter.eq(field, value) filter: Net::LDAP::Filter.eq(field, value)
} }
end end
if config['user_filter'].present? if config.user_filter.present?
user_filter = Net::LDAP::Filter.construct(config['user_filter']) user_filter = Net::LDAP::Filter.construct(config.user_filter)
options[:filter] = if options[:filter] options[:filter] = if options[:filter]
Net::LDAP::Filter.join(options[:filter], user_filter) Net::LDAP::Filter.join(options[:filter], user_filter)
...@@ -72,12 +45,16 @@ module Gitlab ...@@ -72,12 +45,16 @@ module Gitlab
end end
end end
if limit.present?
options.merge!(size: limit)
end
entries = ldap_search(options).select do |entry| entries = ldap_search(options).select do |entry|
entry.respond_to? config.uid entry.respond_to? config.uid
end end
entries.map do |entry| entries.map do |entry|
Gitlab::LDAP::Person.new(entry) Gitlab::LDAP::Person.new(entry, provider)
end end
end end
...@@ -105,12 +82,6 @@ module Gitlab ...@@ -105,12 +82,6 @@ module Gitlab
results results
end end
end end
private
def config
@config ||= self.class.config
end
end end
end end
end end
# This calls helps to authenticate to LDAP by providing username and password
#
# Since multiple LDAP servers are supported, it will loop through all of them
# until a valid bind is found
#
module Gitlab
module LDAP
class Authentication
def self.login(login, password)
return unless Gitlab::LDAP::Config.enabled?
return unless login.present? && password.present?
auth = nil
# loop through providers until valid bind
providers.find do |provider|
auth = new(provider)
auth.login(login, password) # true will exit the loop
end
# If (login, password) was invalid for all providers, the value of auth is now the last
# Gitlab::LDAP::Authentication instance we tried.
auth.user
end
def self.providers
Gitlab::LDAP::Config.providers
end
attr_accessor :provider, :ldap_user
def initialize(provider)
@provider = provider
end
def login(login, password)
@ldap_user = adapter.bind_as(
filter: user_filter(login),
size: 1,
password: password
)
end
def adapter
OmniAuth::LDAP::Adaptor.new(config.options)
end
def config
Gitlab::LDAP::Config.new(provider)
end
def user_filter(login)
filter = Net::LDAP::Filter.eq(config.uid, login)
# Apply LDAP user filter if present
if config.user_filter.present?
filter = Net::LDAP::Filter.join(
filter,
Net::LDAP::Filter.construct(config.user_filter)
)
end
filter
end
def user
return nil unless ldap_user
Gitlab::LDAP::User.find_by_uid_and_provider(ldap_user.dn, provider)
end
end
end
end
\ No newline at end of file
# Load a specific server configuration
module Gitlab
module LDAP
class Config
attr_accessor :provider, :options
def self.enabled?
Gitlab.config.ldap.enabled
end
def self.servers
Gitlab.config.ldap.servers.values
end
def self.providers
servers.map {|server| server['provider_name'] }
end
def initialize(provider)
@provider = provider
invalid_provider unless valid_provider?
@options = config_for(provider)
end
def enabled?
base_config.enabled
end
def adapter_options
{
host: options['host'],
port: options['port'],
encryption: encryption
}.tap do |options|
options.merge!(auth_options) if has_auth?
end
end
def base
options['base']
end
def uid
options['uid']
end
def sync_ssh_keys?
sync_ssh_keys.present?
end
# The LDAP attribute in which the ssh keys are stored
def sync_ssh_keys
options['sync_ssh_keys']
end
def user_filter
options['user_filter']
end
def group_base
options['group_base']
end
def admin_group
options['admin_group']
end
def active_directory
options['active_directory']
end
protected
def base_config
Gitlab.config.ldap
end
def config_for(provider)
base_config.servers.values.find { |server| server['provider_name'] == provider }
end
def encryption
case options['method'].to_s
when 'ssl'
:simple_tls
when 'tls'
:start_tls
else
nil
end
end
def valid_provider?
self.class.providers.include?(provider)
end
def invalid_provider
raise "Unknown provider (#{provider}). Available providers: #{self.class.providers}"
end
def auth_options
{
auth: {
method: :simple,
username: options['bind_dn'],
password: options['password']
}
}
end
def has_auth?
options['password'] || options['bind_dn']
end
end
end
end
...@@ -6,24 +6,24 @@ module Gitlab ...@@ -6,24 +6,24 @@ module Gitlab
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/ # Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2") AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
def self.find_by_uid(uid, adapter=nil) attr_accessor :entry, :provider
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user(config.uid, uid) def self.find_by_uid(uid, adapter)
adapter.user(adapter.config.uid, uid)
end end
def self.find_by_dn(dn, adapter=nil) def self.find_by_dn(dn, adapter)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user('dn', dn) adapter.user('dn', dn)
end end
def self.disabled_via_active_directory?(dn, adapter=nil) def self.disabled_via_active_directory?(dn, adapter)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.dn_matches_filter?(dn, AD_USER_DISABLED) adapter.dn_matches_filter?(dn, AD_USER_DISABLED)
end end
def initialize(entry) def initialize(entry, provider)
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry @entry = entry
@provider = provider
end end
def name def name
...@@ -38,6 +38,10 @@ module Gitlab ...@@ -38,6 +38,10 @@ module Gitlab
uid uid
end end
def email
entry.try(:mail)
end
def dn def dn
entry.dn entry.dn
end end
...@@ -48,12 +52,8 @@ module Gitlab ...@@ -48,12 +52,8 @@ module Gitlab
@entry @entry
end end
def adapter
@adapter ||= Gitlab::LDAP::Adapter.new
end
def config def config
@config ||= Gitlab.config.ldap @config ||= Gitlab::LDAP::Config.new(provider)
end end
end end
end end
......
...@@ -10,77 +10,52 @@ module Gitlab ...@@ -10,77 +10,52 @@ module Gitlab
module LDAP module LDAP
class User < Gitlab::OAuth::User class User < Gitlab::OAuth::User
class << self class << self
def find_or_create(auth_hash) def find_by_uid_and_provider(uid, provider)
self.auth_hash = auth_hash # LDAP distinguished name is case-insensitive
find(auth_hash) || find_and_connect_by_email(auth_hash) || create(auth_hash) ::User.
end where(provider: [provider, :ldap]).
where('lower(extern_uid) = ?', uid.downcase).last
def find_and_connect_by_email(auth_hash)
self.auth_hash = auth_hash
user = model.find_by(email: self.auth_hash.email)
if user
user.update_attributes(extern_uid: auth_hash.uid, provider: auth_hash.provider)
Gitlab::AppLogger.info("(LDAP) Updating legacy LDAP user #{self.auth_hash.email} with extern_uid => #{auth_hash.uid}")
return user
end end
end end
def authenticate(login, password) def initialize(auth_hash)
# Check user against LDAP backend if user is not authenticated super
# Only check with valid login and password to prevent anonymous bind results update_user_attributes
return nil unless ldap_conf.enabled && login.present? && password.present?
ldap_user = adapter.bind_as(
filter: user_filter(login),
size: 1,
password: password
)
find_by_uid(ldap_user.dn) if ldap_user
end end
def adapter # instance methods
@adapter ||= OmniAuth::LDAP::Adaptor.new(ldap_conf) def gl_user
@gl_user ||= find_by_uid_and_provider || find_by_email || build_new_user
end end
protected
def find_by_uid_and_provider def find_by_uid_and_provider
find_by_uid(auth_hash.uid) self.class.find_by_uid_and_provider(
end auth_hash.uid.downcase, auth_hash.provider)
def find_by_uid(uid)
# LDAP distinguished name is case-insensitive
model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last
end
def provider
'ldap'
end end
def raise_error(message) def find_by_email
raise OmniAuth::Error, "(LDAP) " + message model.find_by(email: auth_hash.email)
end end
def ldap_conf def update_user_attributes
Gitlab.config.ldap gl_user.attributes = {
extern_uid: auth_hash.uid,
provider: auth_hash.provider,
email: auth_hash.email
}
end end
def user_filter(login) def changed?
filter = Net::LDAP::Filter.eq(adapter.uid, login) gl_user.changed?
# Apply LDAP user filter if present
if ldap_conf['user_filter'].present?
user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter'])
filter = Net::LDAP::Filter.join(filter, user_filter)
end
filter
end
end end
def needs_blocking? def needs_blocking?
false false
end end
def allowed?
Gitlab::LDAP::Access.allowed?(gl_user)
end
end end
end end
end end
module Gitlab module Gitlab
class Logger < ::Logger class Logger < ::Logger
def self.file_name
file_name_noext + '.log'
end
def self.error(message) def self.error(message)
build.error(message) build.error(message)
end end
......
...@@ -70,14 +70,22 @@ module Gitlab ...@@ -70,14 +70,22 @@ module Gitlab
insert_piece($1) insert_piece($1)
end end
# Context passed to the markdoqwn pipeline # Used markdown pipelines in GitLab:
# GitlabEmojiFilter - performs emoji replacement.
#
# see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
filters = [
HTML::Pipeline::Gitlab::GitlabEmojiFilter
]
markdown_context = { markdown_context = {
asset_root: File.join(root_url, asset_root: Gitlab.config.gitlab.url,
Gitlab::Application.config.assets.prefix) asset_host: Gitlab::Application.config.asset_host
} }
result = HTML::Pipeline::Gitlab::MarkdownPipeline.call(text, markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
markdown_context)
result = markdown_pipeline.call(text, markdown_context)
text = result[:output].to_html(save_with: 0) text = result[:output].to_html(save_with: 0)
allowed_attributes = ActionView::Base.sanitized_allowed_attributes allowed_attributes = ActionView::Base.sanitized_allowed_attributes
......
...@@ -21,7 +21,7 @@ module Gitlab ...@@ -21,7 +21,7 @@ module Gitlab
end end
def name def name
(info.name || full_name).to_s.force_encoding('utf-8') (info.try(:name) || full_name).to_s.force_encoding('utf-8')
end end
def full_name def full_name
......
...@@ -6,55 +6,77 @@ ...@@ -6,55 +6,77 @@
module Gitlab module Gitlab
module OAuth module OAuth
class User class User
class << self attr_accessor :auth_hash, :gl_user
attr_reader :auth_hash
def find(auth_hash) def initialize(auth_hash)
self.auth_hash = auth_hash self.auth_hash = auth_hash
find_by_uid_and_provider
end end
def create(auth_hash) def persisted?
user = new(auth_hash) gl_user.try(:persisted?)
user.save_and_trigger_callbacks
end end
def model def new?
::User !persisted?
end end
def auth_hash=(auth_hash) def valid?
@auth_hash = AuthHash.new(auth_hash) gl_user.try(:valid?)
end end
protected def save
def find_by_uid_and_provider unauthorized_to_create unless gl_user
model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last
if needs_blocking?
gl_user.save!
gl_user.block
else
gl_user.save!
end end
log.info "(OAuth) saving user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}"
gl_user
rescue ActiveRecord::RecordInvalid => e
log.info "(OAuth) Error saving user: #{gl_user.errors.full_messages}"
return self, e.record.errors
end end
# Instance methods def gl_user
attr_accessor :auth_hash, :user @user ||= find_by_uid_and_provider
def initialize(auth_hash) if signup_enabled?
self.auth_hash = auth_hash @user ||= build_new_user
self.user = self.class.model.new(user_attributes) end
user.skip_confirmation!
@user
end
protected
def needs_blocking?
new? && block_after_signup?
end
def signup_enabled?
Gitlab.config.omniauth.allow_single_sign_on
end
def block_after_signup?
Gitlab.config.omniauth.block_auto_created_users
end end
def auth_hash=(auth_hash) def auth_hash=(auth_hash)
@auth_hash = AuthHash.new(auth_hash) @auth_hash = AuthHash.new(auth_hash)
end end
def save_and_trigger_callbacks def find_by_uid_and_provider
user.save! model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last
log.info "(OAuth) Creating user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}" end
user.block if needs_blocking?
user def build_new_user
rescue ActiveRecord::RecordInvalid => e model.new(user_attributes).tap do |user|
log.info "(OAuth) Email #{e.record.errors[:email]}. Username #{e.record.errors[:username]}" user.skip_confirmation!
return nil, e.record.errors end
end end
def user_attributes def user_attributes
...@@ -73,12 +95,12 @@ module Gitlab ...@@ -73,12 +95,12 @@ module Gitlab
Gitlab::AppLogger Gitlab::AppLogger
end end
def raise_error(message) def model
raise OmniAuth::Error, "(OAuth) " + message ::User
end end
def needs_blocking? def raise_unauthorized_to_create
Gitlab.config.omniauth['block_auto_created_users'] raise StandardError.new("Unauthorized to create user, signup disabled for #{auth_hash.provider}")
end end
end end
end end
......
module Gitlab
class ProductionLogger < Gitlab::Logger
def self.file_name_noext
'production'
end
end
end
module Gitlab
class SidekiqLogger < Gitlab::Logger
def self.file_name_noext
'sidekiq'
end
end
end
...@@ -10,6 +10,17 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -10,6 +10,17 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
super options super options
end end
# If project has issue number 39, apostrophe will be linked in
# regular text to the issue as Redcarpet will convert apostrophe to
# #39;
# We replace apostrophe with right single quote before Redcarpet
# does the processing and put the apostrophe back in postprocessing.
# This only influences regular text, code blocks are untouched.
def normal_text(text)
return text unless text.present?
text.gsub("'", "&rsquo;")
end
def block_code(code, language) def block_code(code, language)
# New lines are placed to fix an rendering issue # New lines are placed to fix an rendering issue
# with code wrapped inside <h1> tag for next case: # with code wrapped inside <h1> tag for next case:
...@@ -44,6 +55,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -44,6 +55,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end end
def postprocess(full_document) def postprocess(full_document)
full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil? unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document) full_document = h.create_relative_links(full_document)
end end
......
...@@ -60,17 +60,16 @@ server { ...@@ -60,17 +60,16 @@ server {
client_max_body_size 20m; client_max_body_size 20m;
## Strong SSL Security ## Strong SSL Security
## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
ssl on; ssl on;
ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate /etc/nginx/ssl/gitlab.crt;
ssl_certificate_key /etc/nginx/ssl/gitlab.key; ssl_certificate_key /etc/nginx/ssl/gitlab.key;
ssl_ciphers 'AES256+EECDH:AES256+EDH'; # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
## [WARNING] The following header states that the browser should only communicate ## [WARNING] The following header states that the browser should only communicate
## with your server over a secure connection for the next 24 months. ## with your server over a secure connection for the next 24 months.
...@@ -87,11 +86,10 @@ server { ...@@ -87,11 +86,10 @@ server {
# ssl_stapling_verify on; # ssl_stapling_verify on;
# ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt; # ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt;
# resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired # resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired
# resolver_timeout 10s; # resolver_timeout 5s;
## [Optional] Generate a stronger DHE parameter: ## [Optional] Generate a stronger DHE parameter:
## cd /etc/ssl/certs ## sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
## sudo openssl dhparam -out dhparam.pem 4096
## ##
# ssl_dhparam /etc/ssl/certs/dhparam.pem; # ssl_dhparam /etc/ssl/certs/dhparam.pem;
......
...@@ -664,7 +664,7 @@ namespace :gitlab do ...@@ -664,7 +664,7 @@ namespace :gitlab do
warn_user_is_not_gitlab warn_user_is_not_gitlab
start_checking "LDAP" start_checking "LDAP"
if ldap_config.enabled if Gitlab::LDAP::Config.enabled?
print_users(args.limit) print_users(args.limit)
else else
puts 'LDAP is disabled in config/gitlab.yml' puts 'LDAP is disabled in config/gitlab.yml'
...@@ -675,38 +675,18 @@ namespace :gitlab do ...@@ -675,38 +675,18 @@ namespace :gitlab do
def print_users(limit) def print_users(limit)
puts "LDAP users with access to your GitLab server (only showing the first #{limit} results)" puts "LDAP users with access to your GitLab server (only showing the first #{limit} results)"
ldap.search(attributes: attributes, filter: filter, size: limit, return_result: false) do |entry|
puts "DN: #{entry.dn}\t#{ldap_config.uid}: #{entry[ldap_config.uid]}"
end
end
def attributes servers = Gitlab::LDAP::Config.providers
[ldap_config.uid]
end
def filter
uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
if user_filter
Net::LDAP::Filter.join(uid_filter, user_filter)
else
uid_filter
end
end
def user_filter servers.each do |server|
if ldap_config['user_filter'] && ldap_config.user_filter.present? puts "Server: #{server}"
Net::LDAP::Filter.construct(ldap_config.user_filter) Gitlab::LDAP::Adapter.open(server) do |adapter|
else users = adapter.users(adapter.config.uid, '*', 100)
nil users.each do |user|
puts "\tDN: #{user.dn}\t #{adapter.config.uid}: #{user.uid}"
end end
end end
def ldap
@ldap ||= OmniAuth::LDAP::Adaptor.new(ldap_config).connection
end end
def ldap_config
@ldap_config ||= Gitlab.config.ldap
end end
end end
......
...@@ -34,7 +34,7 @@ namespace :gitlab do ...@@ -34,7 +34,7 @@ namespace :gitlab do
puts "Processing #{repo_path}".yellow puts "Processing #{repo_path}".yellow
if path =~ /.wiki\Z/ if path =~ /\.wiki\Z/
puts " * Skipping wiki repo" puts " * Skipping wiki repo"
next next
end end
......
...@@ -11,7 +11,7 @@ namespace :gitlab do ...@@ -11,7 +11,7 @@ namespace :gitlab do
home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Settings.gitlab.user_home home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Settings.gitlab.user_home
gitlab_url = Settings.gitlab.url gitlab_url = Settings.gitlab.url
# gitlab-shell requires a / at the end of the url # gitlab-shell requires a / at the end of the url
gitlab_url += "/" unless gitlab_url.match(/\/$/) gitlab_url += '/' unless gitlab_url.end_with?('/')
repos_path = Gitlab.config.gitlab_shell.repos_path repos_path = Gitlab.config.gitlab_shell.repos_path
target_dir = Gitlab.config.gitlab_shell.path target_dir = Gitlab.config.gitlab_shell.path
......
...@@ -24,6 +24,11 @@ FactoryGirl.define do ...@@ -24,6 +24,11 @@ FactoryGirl.define do
admin true admin true
end end
trait :ldap do
provider 'ldapmain'
extern_uid 'my-ldap-id'
end
factory :admin, traits: [:admin] factory :admin, traits: [:admin]
end end
......
require 'spec_helper'
describe SnippetsFinder do
let(:user) { create :user }
let(:user1) { create :user }
let(:group) { create :group }
let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, :private, group: group) }
context ':all filter' do
before do
@snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE)
@snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL)
@snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC)
end
it "returns all private and internal snippets" do
snippets = SnippetsFinder.new.execute(user, filter: :all)
snippets.should include(@snippet2, @snippet3)
snippets.should_not include(@snippet1)
end
it "returns all public snippets" do
snippets = SnippetsFinder.new.execute(nil, filter: :all)
snippets.should include(@snippet3)
snippets.should_not include(@snippet1, @snippet2)
end
end
context ':by_user filter' do
before do
@snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE, author: user)
@snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL, author: user)
@snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC, author: user)
end
it "returns all public and internal snippets" do
snippets = SnippetsFinder.new.execute(user1, filter: :by_user, user: user)
snippets.should include(@snippet2, @snippet3)
snippets.should_not include(@snippet1)
end
it "returns internal snippets" do
snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_internal")
snippets.should include(@snippet2)
snippets.should_not include(@snippet1, @snippet3)
end
it "returns private snippets" do
snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_private")
snippets.should include(@snippet1)
snippets.should_not include(@snippet2, @snippet3)
end
it "returns public snippets" do
snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_public")
snippets.should include(@snippet3)
snippets.should_not include(@snippet1, @snippet2)
end
it "returns all snippets" do
snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user)
snippets.should include(@snippet1, @snippet2, @snippet3)
end
end
context 'by_project filter' do
before do
@snippet1 = create(:project_snippet, visibility_level: Snippet::PRIVATE, project: project1)
@snippet2 = create(:project_snippet, visibility_level: Snippet::INTERNAL, project: project1)
@snippet3 = create(:project_snippet, visibility_level: Snippet::PUBLIC, project: project1)
end
it "returns public snippets for unauthorized user" do
snippets = SnippetsFinder.new.execute(nil, filter: :by_project, project: project1)
snippets.should include(@snippet3)
snippets.should_not include(@snippet1, @snippet2)
end
it "returns public and internal snippets for none project members" do
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1)
snippets.should include(@snippet2, @snippet3)
snippets.should_not include(@snippet1)
end
it "returns all snippets for project members" do
project1.team << [user, :developer]
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1)
snippets.should include(@snippet1, @snippet2, @snippet3)
end
end
end
require 'spec_helper'
describe EventsHelper do
include ApplicationHelper
include GitlabMarkdownHelper
it 'should display one line of plain text without alteration' do
input = 'A short, plain note'
expect(event_note(input)).to match(input)
expect(event_note(input)).not_to match(/\.\.\.\z/)
end
it 'should display inline code' do
input = 'A note with `inline code`'
expected = 'A note with <code>inline code</code>'
expect(event_note(input)).to match(expected)
end
it 'should truncate a note with multiple paragraphs' do
input = "Paragraph 1\n\nParagraph 2"
expected = 'Paragraph 1...'
expect(event_note(input)).to match(expected)
end
it 'should display the first line of a code block' do
input = "```\nCode block\nwith two lines\n```"
expected = '<pre><code class="">Code block...</code></pre>'
expect(event_note(input)).to match(expected)
end
it 'should truncate a single long line of text' do
text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
input = "#{text}#{text}#{text}#{text}" # 200 chars
expected = "#{text}#{text}".sub(/.{3}/, '...')
expect(event_note(input)).to match(expected)
end
it 'should preserve a link href when link text is truncated' do
text = 'The quick brown fox jumped over the lazy dog' # 44 chars
input = "#{text}#{text}#{text} " # 133 chars
link_url = 'http://example.com/foo/bar/baz' # 30 chars
input << link_url
expected_link_text = 'http://example...</a>'
expect(event_note(input)).to match(link_url)
expect(event_note(input)).to match(expected_link_text)
end
end
...@@ -60,7 +60,7 @@ describe GitlabMarkdownHelper do ...@@ -60,7 +60,7 @@ describe GitlabMarkdownHelper do
end end
it "should link using a short id" do it "should link using a short id" do
actual = "Backported from #{commit.short_id(6)}" actual = "Backported from #{commit.short_id}"
gfm(actual).should match(expected) gfm(actual).should match(expected)
end end
...@@ -530,6 +530,24 @@ describe GitlabMarkdownHelper do ...@@ -530,6 +530,24 @@ describe GitlabMarkdownHelper do
markdown(actual).should match(%r{<li>light by <a.+>@#{member.user.username}</a></li>}) markdown(actual).should match(%r{<li>light by <a.+>@#{member.user.username}</a></li>})
end end
it "should not link the apostrophe to issue 39" do
project.team << [user, :master]
project.issues.stub(:where).with(iid: '39').and_return([issue])
actual = "Yes, it is @#{member.user.username}'s task."
expected = /Yes, it is <a.+>@#{member.user.username}<\/a>'s task/
markdown(actual).should match(expected)
end
it "should not link the apostrophe to issue 39 in code blocks" do
project.team << [user, :master]
project.issues.stub(:where).with(iid: '39').and_return([issue])
actual = "Yes, `it is @#{member.user.username}'s task.`"
expected = /Yes, <code>it is @gfm\'s task.<\/code>/
markdown(actual).should match(expected)
end
it "should handle references in <em>" do it "should handle references in <em>" do
actual = "Apply _!#{merge_request.iid}_ ASAP" actual = "Apply _!#{merge_request.iid}_ ASAP"
...@@ -576,9 +594,21 @@ describe GitlabMarkdownHelper do ...@@ -576,9 +594,21 @@ describe GitlabMarkdownHelper do
end end
it "should generate absolute urls for emoji" do it "should generate absolute urls for emoji" do
markdown(":smile:").should include("src=\"#{url_helper('emoji/smile')}") markdown(":smile:").should include("src=\"http://localhost/assets/emoji/smile.png")
end
it "should generate absolute urls for emoji if relative url is present" do
Gitlab.config.gitlab.stub(:url).and_return('http://localhost/gitlab/root')
markdown(":smile:").should include("src=\"http://localhost/gitlab/root/assets/emoji/smile.png")
end end
it "should generate absolute urls for emoji if asset_host is present" do
Gitlab::Application.config.stub(:asset_host).and_return("https://cdn.example.com")
ActionView::Base.any_instance.stub_chain(:config, :asset_host).and_return("https://cdn.example.com")
markdown(":smile:").should include("src=\"https://cdn.example.com/assets/emoji/smile.png")
end
it "should handle relative urls for a file in master" do it "should handle relative urls for a file in master" do
actual = "[GitLab API doc](doc/api/README.md)\n" actual = "[GitLab API doc](doc/api/README.md)\n"
expected = "<p><a href=\"/#{project.path_with_namespace}/blob/#{@ref}/doc/api/README.md\">GitLab API doc</a></p>\n" expected = "<p><a href=\"/#{project.path_with_namespace}/blob/#{@ref}/doc/api/README.md\">GitLab API doc</a></p>\n"
......
...@@ -28,17 +28,16 @@ describe Gitlab::Auth do ...@@ -28,17 +28,16 @@ describe Gitlab::Auth do
end end
context "with ldap enabled" do context "with ldap enabled" do
before { Gitlab.config.ldap['enabled'] = true } before { Gitlab::LDAP::Config.stub(enabled?: true) }
after { Gitlab.config.ldap['enabled'] = false }
it "tries to autheticate with db before ldap" do it "tries to autheticate with db before ldap" do
expect(Gitlab::LDAP::User).not_to receive(:authenticate) expect(Gitlab::LDAP::Authentication).not_to receive(:login)
gl_auth.find(username, password) gl_auth.find(username, password)
end end
it "uses ldap as fallback to for authentication" do it "uses ldap as fallback to for authentication" do
expect(Gitlab::LDAP::User).to receive(:authenticate) expect(Gitlab::LDAP::Authentication).to receive(:login)
gl_auth.find('ldap_user', 'password') gl_auth.find('ldap_user', 'password')
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Access do describe Gitlab::LDAP::Access do
let(:access) { Gitlab::LDAP::Access.new } let(:access) { Gitlab::LDAP::Access.new user }
let(:user) { create(:user) } let(:user) { create(:user, :ldap) }
describe :allowed? do describe :allowed? do
subject { access.allowed?(user) } subject { access.allowed? }
context 'when the user cannot be found' do context 'when the user cannot be found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: nil) } before { Gitlab::LDAP::Person.stub(find_by_dn: nil) }
...@@ -28,19 +28,13 @@ describe Gitlab::LDAP::Access do ...@@ -28,19 +28,13 @@ describe Gitlab::LDAP::Access do
it { should be_true } it { should be_true }
end end
context 'and has no disabled flag in active diretory' do context 'without ActiveDirectory enabled' do
before { before do
Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false) Gitlab::LDAP::Config.stub(enabled?: true)
Gitlab.config.ldap['enabled'] = true Gitlab::LDAP::Config.any_instance.stub(active_directory: false)
Gitlab.config.ldap['active_directory'] = false end
}
after {
Gitlab.config.ldap['enabled'] = false
Gitlab.config.ldap['active_directory'] = true
}
it { should be_false } it { should be_true }
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Adapter do describe Gitlab::LDAP::Adapter do
let(:adapter) { Gitlab::LDAP::Adapter.new } let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
describe :dn_matches_filter? do describe :dn_matches_filter? do
let(:ldap) { double(:ldap) } let(:ldap) { double(:ldap) }
......
require 'spec_helper'
describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication }
let(:user) { create(:user, :ldap, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
let(:password) { 'password' }
describe :login do
let(:adapter) { double :adapter }
before do
Gitlab::LDAP::Config.stub(enabled?: true)
end
it "finds the user if authentication is successful" do
user
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_true
end
it "is false if the user does not exist" do
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_false
end
it "is false if authentication fails" do
user
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, bind_as: nil))
expect(klass.login(login, password)).to be_false
end
it "fails if ldap is disabled" do
Gitlab::LDAP::Config.stub(enabled?: false)
expect(klass.login(login, password)).to be_false
end
it "fails if no login is supplied" do
expect(klass.login('', password)).to be_false
end
it "fails if no password is supplied" do
expect(klass.login(login, '')).to be_false
end
end
end
\ No newline at end of file
require 'spec_helper'
describe Gitlab::LDAP::Config do
let(:config) { Gitlab::LDAP::Config.new provider }
let(:provider) { 'ldapmain' }
describe :initalize do
it 'requires a provider' do
expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError
end
it "works" do
expect(config).to be_a described_class
end
it "raises an error if a unknow provider is used" do
expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error
end
end
end
\ No newline at end of file
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::User do describe Gitlab::LDAP::User do
let(:gl_user) { Gitlab::LDAP::User } let(:gl_user) { Gitlab::LDAP::User.new(auth_hash) }
let(:info) do let(:info) do
double( {
name: 'John', name: 'John',
email: 'john@example.com', email: 'john@example.com',
nickname: 'john' nickname: 'john'
) }
end end
before { Gitlab.config.stub(omniauth: {}) } let(:auth_hash) do
double(uid: 'my-uid', provider: 'ldapmain', info: double(info))
describe :find_or_create do
let(:auth) do
double(info: info, provider: 'ldap', uid: 'my-uid')
end end
describe :find_or_create do
it "finds the user if already existing" do it "finds the user if already existing" do
existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldap') existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldapmain')
expect{ gl_user.find_or_create(auth) }.to_not change{ User.count } expect{ gl_user.save }.to_not change{ User.count }
end end
it "connects to existing non-ldap user if the email matches" do it "connects to existing non-ldap user if the email matches" do
existing_user = create(:user, email: 'john@example.com') existing_user = create(:user, email: 'john@example.com')
expect{ gl_user.find_or_create(auth) }.to_not change{ User.count } expect{ gl_user.save }.to_not change{ User.count }
existing_user.reload existing_user.reload
expect(existing_user.extern_uid).to eql 'my-uid' expect(existing_user.extern_uid).to eql 'my-uid'
expect(existing_user.provider).to eql 'ldap' expect(existing_user.provider).to eql 'ldapmain'
end end
it "creates a new user if not found" do it "creates a new user if not found" do
expect{ gl_user.find_or_create(auth) }.to change{ User.count }.by(1) expect{ gl_user.save }.to change{ User.count }.by(1)
end
end
describe "authenticate" do
let(:login) { 'john' }
let(:password) { 'my-secret' }
before {
Gitlab.config.ldap['enabled'] = true
Gitlab.config.ldap['user_filter'] = 'employeeType=developer'
}
after { Gitlab.config.ldap['enabled'] = false }
it "send an authentication request to ldap" do
expect( Gitlab::LDAP::User.adapter ).to receive(:bind_as)
Gitlab::LDAP::User.authenticate(login, password)
end end
end end
end end
require 'spec_helper'
describe Gitlab::OAuth::AuthHash do
let(:auth_hash) do
Gitlab::OAuth::AuthHash.new(double({
provider: 'twitter',
uid: uid,
info: double(info_hash)
}))
end
let(:uid) { 'my-uid' }
let(:email) { 'my-email@example.com' }
let(:nickname) { 'my-nickname' }
let(:info_hash) {
{
email: email,
nickname: nickname,
name: 'John',
first_name: "John",
last_name: "Who"
}
}
context "defaults" do
it { expect(auth_hash.provider).to eql 'twitter' }
it { expect(auth_hash.uid).to eql uid }
it { expect(auth_hash.email).to eql email }
it { expect(auth_hash.username).to eql nickname }
it { expect(auth_hash.name).to eql "John" }
it { expect(auth_hash.password).to_not be_empty }
end
context "email not provided" do
before { info_hash.delete(:email) }
it "generates a temp email" do
expect( auth_hash.email).to start_with('temp-email-for-oauth')
end
end
context "username not provided" do
before { info_hash.delete(:nickname) }
it "takes the first part of the email as username" do
expect( auth_hash.username ).to eql "my-email"
end
end
context "name not provided" do
before { info_hash.delete(:name) }
it "concats first and lastname as the name" do
expect( auth_hash.name ).to eql "John Who"
end
end
end
\ No newline at end of file
require 'spec_helper' require 'spec_helper'
describe Gitlab::OAuth::User do describe Gitlab::OAuth::User do
let(:gl_auth) { Gitlab::OAuth::User } let(:oauth_user) { Gitlab::OAuth::User.new(auth_hash) }
let(:info) do let(:gl_user) { oauth_user.gl_user }
double( let(:uid) { 'my-uid' }
let(:provider) { 'my-provider' }
let(:auth_hash) { double(uid: uid, provider: provider, info: double(info_hash)) }
let(:info_hash) do
{
nickname: 'john', nickname: 'john',
name: 'John', name: 'John',
email: 'john@mail.com' email: 'john@mail.com'
) }
end end
before do describe :persisted? do
Gitlab.config.stub(omniauth: {})
end
describe :find do
let!(:existing_user) { create(:user, extern_uid: 'my-uid', provider: 'my-provider') } let!(:existing_user) { create(:user, extern_uid: 'my-uid', provider: 'my-provider') }
it "finds an existing user based on uid and provider (facebook)" do it "finds an existing user based on uid and provider (facebook)" do
auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider') auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider')
assert gl_auth.find(auth) expect( oauth_user.persisted? ).to be_true
end end
it "finds an existing user based on nested uid and provider" do it "returns false if use is not found in database" do
auth = double(info: info, uid: 'my-uid', provider: 'my-provider') auth_hash.stub(uid: 'non-existing')
assert gl_auth.find(auth) expect( oauth_user.persisted? ).to be_false
end end
end end
describe :create do describe :save do
it "should create user from LDAP" do let(:provider) { 'twitter' }
auth = double(info: info, uid: 'my-uid', provider: 'ldap')
user = gl_auth.create(auth) describe 'signup' do
context "with allow_single_sign_on enabled" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
it "creates a user from Omniauth" do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user.extern_uid).to eql uid
expect(gl_user.provider).to eql 'twitter'
end
end
user.should be_valid context "with allow_single_sign_on disabled (Default)" do
user.extern_uid.should == auth.uid it "throws an error" do
user.provider.should == 'ldap' expect{ oauth_user.save }.to raise_error StandardError
end
end end
end
describe 'blocking' do
let(:provider) { 'twitter' }
before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
it "should create user from Omniauth" do context 'signup' do
auth = double(info: info, uid: 'my-uid', provider: 'twitter') context 'dont block on create' do
user = gl_auth.create(auth) before { Gitlab.config.omniauth.stub block_auto_created_users: false }
user.should be_valid it do
user.extern_uid.should == auth.uid oauth_user.save
user.provider.should == 'twitter' gl_user.should be_valid
gl_user.should_not be_blocked
end
end end
it "should apply defaults to user" do context 'block on create' do
auth = double(info: info, uid: 'my-uid', provider: 'ldap') before { Gitlab.config.omniauth.stub block_auto_created_users: true }
user = gl_auth.create(auth)
user.should be_valid it do
user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit oauth_user.save
user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group gl_user.should be_valid
gl_user.should be_blocked
end
end
end end
it "Set a temp email address if not provided (like twitter does)" do context 'sign-in' do
info = double( before do
uid: 'my-uid', oauth_user.save
nickname: 'john', oauth_user.gl_user.activate
name: 'John' end
)
auth = double(info: info, uid: 'my-uid', provider: 'my-provider') context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false }
user = gl_auth.create(auth) it do
expect(user.email).to_not be_empty oauth_user.save
gl_user.should be_valid
gl_user.should_not be_blocked
end
end end
it 'generates a username if non provided (google)' do context 'block on create' do
info = double( before { Gitlab.config.omniauth.stub block_auto_created_users: true }
uid: 'my-uid',
name: 'John',
email: 'john@example.com'
)
auth = double(info: info, uid: 'my-uid', provider: 'my-provider')
user = gl_auth.create(auth) it do
expect(user.username).to eql 'john' oauth_user.save
gl_user.should be_valid
gl_user.should_not be_blocked
end
end
end
end end
end end
end end
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -75,7 +75,7 @@ eos ...@@ -75,7 +75,7 @@ eos
it_behaves_like 'a mentionable' do it_behaves_like 'a mentionable' do
let(:subject) { commit } let(:subject) { commit }
let(:mauthor) { create :user, email: commit.author_email } let(:mauthor) { create :user, email: commit.author_email }
let(:backref_text) { "commit #{subject.sha[0..5]}" } let(:backref_text) { "commit #{subject.id}" }
let(:set_mentionable_text) { ->(txt){ subject.stub(safe_message: txt) } } let(:set_mentionable_text) { ->(txt){ subject.stub(safe_message: txt) } }
# Include the subject in the repository stub. # Include the subject in the repository stub.
......
# == Schema Information # == Schema Information
# #
# Table name: group_members # Table name: members
# #
# id :integer not null, primary key # id :integer not null, primary key
# access_level :integer not null # access_level :integer not null
# group_id :integer not null # source_id :integer not null
# source_type :string(255) not null
# user_id :integer not null # user_id :integer not null
# notification_level :integer not null
# type :string(255)
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# notification_level :integer default(3), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -228,7 +228,7 @@ describe Note do ...@@ -228,7 +228,7 @@ describe Note do
it { should be_valid } it { should be_valid }
its(:noteable) { should == issue } its(:noteable) { should == issue }
its(:note) { should == "_mentioned in commit #{commit.sha[0..5]}_" } its(:note) { should == "_mentioned in commit #{commit.sha}_" }
end end
context 'merge request from an issue' do context 'merge request from an issue' do
...@@ -267,7 +267,7 @@ describe Note do ...@@ -267,7 +267,7 @@ describe Note do
its(:noteable_type) { should == "Commit" } its(:noteable_type) { should == "Commit" }
its(:noteable_id) { should be_nil } its(:noteable_id) { should be_nil }
its(:commit_id) { should == commit.id } its(:commit_id) { should == commit.id }
its(:note) { should == "_mentioned in commit #{parent_commit.id[0...6]}_" } its(:note) { should == "_mentioned in commit #{parent_commit.id}_" }
end end
end end
......
# == Schema Information # == Schema Information
# #
# Table name: project_members # Table name: members
# #
# id :integer not null, primary key # id :integer not null, primary key
# access_level :integer not null
# source_id :integer not null
# source_type :string(255) not null
# user_id :integer not null # user_id :integer not null
# project_id :integer not null # notification_level :integer not null
# type :string(255)
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# project_access :integer default(0), not null
# notification_level :integer default(3), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
# updated_at :datetime # updated_at :datetime
# file_name :string(255) # file_name :string(255)
# expires_at :datetime # expires_at :datetime
# private :boolean default(TRUE), not null
# type :string(255) # type :string(255)
# visibility_level :integer default(0), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -27,6 +27,8 @@ describe ProjectTeam do ...@@ -27,6 +27,8 @@ describe ProjectTeam do
it { project.team.master?(guest).should be_false } it { project.team.master?(guest).should be_false }
it { project.team.master?(reporter).should be_false } it { project.team.master?(reporter).should be_false }
it { project.team.master?(nonmember).should be_false } it { project.team.master?(nonmember).should be_false }
it { project.team.member?(nonmember).should be_false }
it { project.team.member?(guest).should be_true }
end end
end end
...@@ -60,6 +62,8 @@ describe ProjectTeam do ...@@ -60,6 +62,8 @@ describe ProjectTeam do
it { project.team.master?(guest).should be_true } it { project.team.master?(guest).should be_true }
it { project.team.master?(reporter).should be_false } it { project.team.master?(reporter).should be_false }
it { project.team.master?(nonmember).should be_false } it { project.team.master?(nonmember).should be_false }
it { project.team.member?(nonmember).should be_false }
it { project.team.member?(guest).should be_true }
end end
end end
end end
......
...@@ -77,5 +77,25 @@ describe SlackService do ...@@ -77,5 +77,25 @@ describe SlackService do
WebMock.should have_requested(:post, api_url).once WebMock.should have_requested(:post, api_url).once
end end
end end
context 'with new webhook syntax with slack allowed team name' do
before do
@allowed_webhook = 'https://gitlab-hq-123.slack.com/services/hooks/incoming-webhook?token=cdIj4r4LfXUOySDUjp0tk3OI'
slack_service.stub(
project: project,
project_id: project.id,
service_hook: true,
webhook: @allowed_webhook
)
WebMock.stub_request(:post, @allowed_webhook)
end
it "should call Slack API" do
slack_service.execute(sample_data)
WebMock.should have_requested(:post, @allowed_webhook).once
end
end
end end
end end
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
# updated_at :datetime # updated_at :datetime
# file_name :string(255) # file_name :string(255)
# expires_at :datetime # expires_at :datetime
# private :boolean default(TRUE), not null
# type :string(255) # type :string(255)
# visibility_level :integer default(0), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -346,6 +346,25 @@ describe User do ...@@ -346,6 +346,25 @@ describe User do
end end
end end
describe :ldap_user? do
let(:user) { build(:user, :ldap) }
it "is true if provider name starts with ldap" do
user.provider = 'ldapmain'
expect( user.ldap_user? ).to be_true
end
it "is false for other providers" do
user.provider = 'other-provider'
expect( user.ldap_user? ).to be_false
end
it "is false if no extern_uid is provided" do
user.extern_uid = nil
expect( user.ldap_user? ).to be_false
end
end
describe '#full_website_url' do describe '#full_website_url' do
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -429,4 +448,32 @@ describe User do ...@@ -429,4 +448,32 @@ describe User do
expect(user.starred?(project)).to be_false expect(user.starred?(project)).to be_false
end end
end end
describe "#sort" do
before do
User.delete_all
@user = create :user, created_at: Date.today, last_sign_in_at: Date.today, name: 'Alpha'
@user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega'
end
it "sorts users as recently_signed_in" do
User.sort('recent_sign_in').first.should == @user
end
it "sorts users as late_signed_in" do
User.sort('oldest_sign_in').first.should == @user1
end
it "sorts users as recently_created" do
User.sort('recently_created').first.should == @user
end
it "sorts users as late_created" do
User.sort('late_created').first.should == @user1
end
it "sorts users by name when nil is passed" do
User.sort(nil).first.should == @user
end
end
end end
...@@ -5,10 +5,11 @@ describe API::API, api: true do ...@@ -5,10 +5,11 @@ describe API::API, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:key) { create(:key, user: user) } let(:key) { create(:key, user: user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:secret_token) { File.read Rails.root.join('.gitlab_shell_secret') }
describe "GET /internal/check", no_db: true do describe "GET /internal/check", no_db: true do
it do it do
get api("/internal/check") get api("/internal/check"), secret_token: secret_token
response.status.should == 200 response.status.should == 200
json_response['api_version'].should == API::API.version json_response['api_version'].should == API::API.version
...@@ -17,7 +18,7 @@ describe API::API, api: true do ...@@ -17,7 +18,7 @@ describe API::API, api: true do
describe "GET /internal/discover" do describe "GET /internal/discover" do
it do it do
get(api("/internal/discover"), key_id: key.id) get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
response.status.should == 200 response.status.should == 200
...@@ -159,7 +160,8 @@ describe API::API, api: true do ...@@ -159,7 +160,8 @@ describe API::API, api: true do
api("/internal/allowed"), api("/internal/allowed"),
key_id: key.id, key_id: key.id,
project: project.path_with_namespace, project: project.path_with_namespace,
action: 'git-upload-pack' action: 'git-upload-pack',
secret_token: secret_token
) )
end end
...@@ -169,7 +171,8 @@ describe API::API, api: true do ...@@ -169,7 +171,8 @@ describe API::API, api: true do
changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master', changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
key_id: key.id, key_id: key.id,
project: project.path_with_namespace, project: project.path_with_namespace,
action: 'git-receive-pack' action: 'git-receive-pack',
secret_token: secret_token
) )
end end
...@@ -179,7 +182,8 @@ describe API::API, api: true do ...@@ -179,7 +182,8 @@ describe API::API, api: true do
ref: 'master', ref: 'master',
key_id: key.id, key_id: key.id,
project: project.path_with_namespace, project: project.path_with_namespace,
action: 'git-upload-archive' action: 'git-upload-archive',
secret_token: secret_token
) )
end end
end end
...@@ -27,4 +27,30 @@ describe API::API, api: true do ...@@ -27,4 +27,30 @@ describe API::API, api: true do
project.gitlab_ci_service.should be_nil project.gitlab_ci_service.should be_nil
end end
end end
describe 'PUT /projects/:id/services/hipchat' do
it 'should update hipchat settings' do
put api("/projects/#{project.id}/services/hipchat", user),
token: 'secret-token', room: 'test'
response.status.should == 200
project.hipchat_service.should_not be_nil
end
it 'should return if required fields missing' do
put api("/projects/#{project.id}/services/gitlab-ci", user),
token: 'secret-token', active: true
response.status.should == 400
end
end
describe 'DELETE /projects/:id/services/hipchat' do
it 'should delete hipchat settings' do
delete api("/projects/#{project.id}/services/hipchat", user)
response.status.should == 200
project.hipchat_service.should be_nil
end
end
end end
...@@ -30,15 +30,15 @@ def common_mentionable_setup ...@@ -30,15 +30,15 @@ def common_mentionable_setup
"!#{mentioned_mr.iid}, " + "!#{mentioned_mr.iid}, " +
"#{ext_proj.path_with_namespace}##{ext_issue.iid}, " + "#{ext_proj.path_with_namespace}##{ext_issue.iid}, " +
"#{ext_proj.path_with_namespace}!#{ext_mr.iid}, " + "#{ext_proj.path_with_namespace}!#{ext_mr.iid}, " +
"#{ext_proj.path_with_namespace}@#{ext_commit.id[0..5]}, " + "#{ext_proj.path_with_namespace}@#{ext_commit.short_id}, " +
"#{mentioned_commit.sha[0..5]} and itself as #{backref_text}" "#{mentioned_commit.sha[0..10]} and itself as #{backref_text}"
end end
before do before do
# Wire the project's repository to return the mentioned commit, and +nil+ for any # Wire the project's repository to return the mentioned commit, and +nil+ for any
# unrecognized commits. # unrecognized commits.
commitmap = { '123456' => mentioned_commit } commitmap = { '1234567890a' => mentioned_commit }
extra_commits.each { |c| commitmap[c.sha[0..5]] = c } extra_commits.each { |c| commitmap[c.short_id] = c }
mproject.repository.stub(:commit) { |sha| commitmap[sha] } mproject.repository.stub(:commit) { |sha| commitmap[sha] }
set_mentionable_text.call(ref_string) set_mentionable_text.call(ref_string)
end end
...@@ -54,7 +54,6 @@ shared_examples 'a mentionable' do ...@@ -54,7 +54,6 @@ shared_examples 'a mentionable' do
it "extracts references from its reference property" do it "extracts references from its reference property" do
# De-duplicate and omit itself # De-duplicate and omit itself
refs = subject.references(mproject) refs = subject.references(mproject)
refs.should have(6).items refs.should have(6).items
refs.should include(mentioned_issue) refs.should include(mentioned_issue)
refs.should include(mentioned_mr) refs.should include(mentioned_mr)
...@@ -90,7 +89,7 @@ shared_examples 'an editable mentionable' do ...@@ -90,7 +89,7 @@ shared_examples 'an editable mentionable' do
it 'creates new cross-reference notes when the mentionable text is edited' do it 'creates new cross-reference notes when the mentionable text is edited' do
new_text = "still mentions ##{mentioned_issue.iid}, " + new_text = "still mentions ##{mentioned_issue.iid}, " +
"#{mentioned_commit.sha[0..5]}, " + "#{mentioned_commit.sha[0..10]}, " +
"#{ext_issue.iid}, " + "#{ext_issue.iid}, " +
"new refs: ##{other_issue.iid}, " + "new refs: ##{other_issue.iid}, " +
"#{ext_proj.path_with_namespace}##{other_ext_issue.iid}" "#{ext_proj.path_with_namespace}##{other_ext_issue.iid}"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment