Commit f91d0e09 authored by Ruben Davila's avatar Ruben Davila

Merge branch 'master' into time-tracking-integration

Conflicts:
	app/helpers/issuables_helper.rb
parents dcb60557 da0906c3
CHANGELOG.md merge=union
CHANGELOG-EE.md merge=union
*.js.es6 gitlab-language=javascript
......@@ -2,36 +2,21 @@ Please view this file on the master branch, on stable branches it's out of date.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- Gracefully recover from previously failed rebase.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- Disable retries for remote mirror update worker. !848
- Fix Approvals API documentation.
- Add ability to set approvals_before_merge for project through the API.
## 8.14.0 (2016-11-22)
- gitlab:check rake task checks ES version according to requirements
- Convert ASCII-8BIT LDAP DNs to UTF-8 to avoid unnecessary user deletions
- [Fix] Only owner can see "Projects" button in group edit menu
## 8.13.6 (2016-11-17)
- No changes.
- Disable retries for remote mirror update worker. !848
- Fixed cache clearing on secondary Geo nodes. !869
- Geo: fix a problem that prevented git cloning from secondary node. !873
## 8.13.5 (2016-11-08)
- No changes
......
......@@ -4,18 +4,6 @@ entry.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- No changes.
## 8.14.0 (2016-11-22)
- Centralize LDAP config/filter logic. !6606
- Show random messages when the To Do list is empty. !6818 (Josep Llaneras)
- Fix record not found error on NewNoteWorker processing. !6863 (Oswaldo Ferreira)
......@@ -37,9 +25,6 @@ entry.
- Remove additional padding on right-aligned items in MR widget. !7411 (Didem Acet)
- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
- Fix project Visibility Level selector not using default values.
## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
......@@ -142,6 +127,18 @@ entry.
- Updated commit SHA styling on the branches page.
- Fix 404 when visit /projects page
## 8.13.6 (2016-11-17)
- No changes.
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
- Fix cache for commit status in commits list to respect branches. !7372
- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
- Limit labels returned for a specific project as an administrator. !7496
- Clicking "force remove source branch" label now toggles the checkbox again.
- Allow commit note to be visible if repo is visible.
- Fix project Visibility Level selector not using default values.
## 8.13.5 (2016-11-08)
- Restore unauthenticated access to public container registries
......
......@@ -39,11 +39,6 @@ module IssuablesHelper
end
end
def can_add_template?(issuable)
names = issuable_templates(issuable)
names.empty? && can?(current_user, :push_code, @project) && !@project.private?
end
def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template"
options = {
......
module Geo
class ScheduleRepoUpdateService
attr_reader :id, :clone_url
attr_reader :id, :clone_url, :push_data
def initialize(params)
@id = params[:project_id]
@clone_url = params[:project][:git_ssh_url]
@push_data = { 'type' => params[:object_kind], 'before' => params[:before],
'after' => params[:newref], 'ref' => params[:ref] }
end
def execute
GeoRepositoryUpdateWorker.perform_async(@id, @clone_url)
GeoRepositoryUpdateWorker.perform_async(@id, @clone_url, @push_data)
end
end
end
......@@ -14,7 +14,7 @@ module Users
private
def record_activity
user_activity.touch
user_activity.touch unless Gitlab::Geo.secondary?
Rails.logger.debug("Recorded activity: #{@activity} for User ID: #{@author.id} (username: #{@author.username}")
end
......
......@@ -3,7 +3,7 @@
Geo Nodes
%p.light
With #{link_to 'GitLab Geo', help_page_path('gitlab-geo', 'README'), class: 'vlink'} you can install a special
With #{link_to 'GitLab Geo', help_page_path('gitlab-geo/README'), class: 'vlink'} you can install a special
read-only and replicated instance anywhere.
%hr
......@@ -31,7 +31,7 @@
= fg.text_area :key, class: 'form-control thin_area', rows: 5
%p.help-block
Paste a machine public key here for the GitLab user this node runs on. Read more about how to generate it
= link_to "here", help_page_path("ssh", "README")
= link_to "here", help_page_path("ssh/README")
.form-actions
= f.submit 'Add Node', class: 'btn btn-create'
......
- form = local_assigns.fetch(:f)
- project = @target_project || @project
= form_errors(issuable)
......@@ -10,44 +11,17 @@
and make sure your changes will not unintentionally remove theirs
.form-group
= f.label :title, class: 'control-label'
= form.label :title, class: 'control-label'
= render 'shared/issuable/form/template_selector', issuable: issuable
%div{ class: issuable_templates(issuable).any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
class: 'form-control pad', required: true
- if issuable.is_a?(MergeRequest)
%p.help-block
.js-wip-explanation
%a.js-toggle-wip{href: "", tabindex: -1}
Remove the
%code WIP:
prefix from the title
to allow this
%strong Work In Progress
merge request to be merged when it's ready.
.js-no-wip-explanation
%a.js-toggle-wip{href: "", tabindex: -1}
Start the title with
%code WIP:
to prevent a
%strong Work In Progress
merge request from being merged before it's ready.
- if can_add_template?(issuable)
%p.help-block
Add
= link_to "description templates", help_page_path('user/project/description_templates'), tabindex: -1
to help your contributors communicate effectively!
= render 'shared/issuable/form/title', issuable: issuable, form: form
.form-group.detail-page-description
= f.label :description, 'Description', class: 'control-label'
= form.label :description, 'Description', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description,
= render 'projects/zen', f: form, attr: :description,
classes: 'note-textarea',
placeholder: "Write a comment or drag your files here...",
supports_slash_commands: !issuable.persisted?
......@@ -59,8 +33,8 @@
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :confidential do
= f.check_box :confidential
= form.label :confidential do
= form.check_box :confidential
This issue is confidential and should only be visible to team members with at least Reporter access.
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
......@@ -69,22 +43,22 @@
.row
%div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
.form-group.issue-assignee
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
= form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- if issuable.assignee_id
= f.hidden_field :assignee_id
= form.hidden_field :assignee_id
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
.form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
= form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group
- has_labels = @labels && @labels.any?
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
= f.hidden_field :label_ids, multiple: true, value: ''
= form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
= form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label"
......@@ -94,12 +68,12 @@
- weight_options.delete(Issue::WEIGHT_ALL)
- weight_options.delete(Issue::WEIGHT_ANY)
.form-group
= f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
= form.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
Weight
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- if issuable.weight
= f.hidden_field :weight
= form.hidden_field :weight
= dropdown_tag(issuable.weight || "Weight", options: { title: "Select weight", toggle_class: 'js-weight-select js-issuable-form-weight', dropdown_class: "dropdown-menu-selectable dropdown-menu-weight",
placeholder: "Search weight", data: { field_name: "#{issuable.class.model_name.param_key}[weight]" , default_label: "Weight" } }) do
%ul
......@@ -111,10 +85,10 @@
- if has_due_date
.col-lg-6
.form-group
= f.label :due_date, "Due date", class: "control-label"
= form.label :due_date, "Due date", class: "control-label"
.col-sm-10
.issuable-form-select-holder
= f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
= form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
- if issuable.can_move?(current_user)
%hr
......@@ -128,21 +102,21 @@
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
= icon('question-circle')
= render 'shared/issuable/approvals', issuable: issuable, f: f
= render 'shared/issuable/approvals', issuable: issuable, f: form
- if issuable.is_a?(MergeRequest) && !issuable.closed_without_fork?
%hr
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label'
= form.label :source_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
= form.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
= f.label :target_branch, class: 'control-label'
= form.label :target_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
= form.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record?
 
= link_to 'Change branches', mr_change_branches_path(@merge_request)
......@@ -158,9 +132,9 @@
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
- if issuable.new_record?
= f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
= form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
- else
= f.submit 'Save changes', class: 'btn btn-save'
= form.submit 'Save changes', class: 'btn btn-save'
- if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project))
.inline.prepend-left-10
......@@ -184,4 +158,4 @@
= icon("sign-out")
Remove
= f.hidden_field :lock_version
= form.hidden_field :lock_version
- issuable = local_assigns.fetch(:issuable)
- form = local_assigns.fetch(:form)
- no_issuable_templates = issuable_templates(issuable).empty?
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
autocomplete: 'off', class: 'form-control pad'
- if issuable.respond_to?(:work_in_progress?)
%p.help-block
.js-wip-explanation
%a.js-toggle-wip{ href: '', tabindex: -1 }
Remove the
%code WIP:
prefix from the title
to allow this
%strong Work In Progress
merge request to be merged when it's ready.
.js-no-wip-explanation
%a.js-toggle-wip{ href: '', tabindex: -1 }
Start the title with
%code WIP:
to prevent a
%strong Work In Progress
merge request from being merged before it's ready.
- if no_issuable_templates && can?(current_user, :push_code, issuable.project)
%p.help-block
Add
= link_to 'description templates', help_page_path('user/project/description_templates'), tabindex: -1
to help your contributors communicate effectively!
......@@ -5,16 +5,46 @@ class GeoRepositoryUpdateWorker
attr_accessor :project
def perform(project_id, clone_url)
def perform(project_id, clone_url, push_data = nil)
@project = Project.find(project_id)
@push_data = push_data
fetch_repository(clone_url)
process_hooks if push_data # we should be compatible with old unprocessed data
end
private
def fetch_repository(remote_url)
@project.create_repository unless @project.repository_exists?
@project.repository.after_create if @project.empty_repo?
@project.repository.fetch_geo_mirror(remote_url)
end
def process_hooks
if @push_data['type'] == 'push'
branch = Gitlab::Git.ref_name(@push_data['ref'])
process_push(branch, @push_data['after'])
end
end
def process_push(branch, revision)
@project.repository.after_push_commit(branch, revision)
if push_remove_branch?
@project.repository.after_remove_branch
elsif push_to_new_branch?
@project.repository.after_create_branch
end
ProjectCacheWorker.perform_async(@project.id)
end
def push_remove_branch?
Gitlab::Git.branch_ref?(@push_data['ref']) && Gitlab::Git.blank_ref?(@push_data['after'])
end
def push_to_new_branch?
Gitlab::Git.branch_ref?(@push_data['ref']) && Gitlab::Git.blank_ref?(@push_data['before'])
end
end
......@@ -6,6 +6,7 @@
class ProjectCacheWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
prepend EE::Workers::ProjectCacheWorker
LEASE_TIMEOUT = 15.minutes.to_i
......
......@@ -117,6 +117,41 @@ production:
[Restart GitLab][restart] for the changes to take effect.
## External Groups
>**Note:** External Groups configuration is only available in GitLab EE Version
8.9 and above.
Using the `external_groups` setting will allow you to mark all users belonging
to these groups as [external users](../../user/permissions.md). Group membership is
checked periodically through the `LdapGroupSync` background task.
**Omnibus configuration**
```ruby
gitlab_rails['ldap_servers'] = YAML.load <<-EOS
main:
# snip...
external_groups: ['interns', 'contractors']
EOS
```
[Reconfigure GitLab][reconfigure] for the changes to take effect.
**Source configuration**
```yaml
production:
ldap:
servers:
main:
# snip...
external_groups: ['interns', 'contractors']
```
[Restart GitLab][restart] for the changes to take effect.
## Group Sync Technical Details
There is a lot going on with group sync 'under the hood'. This section
......@@ -178,6 +213,8 @@ network and LDAP server response time will affect these metrics.
## Troubleshooting
### Referral Error
If you see `LDAP search error: Referral` in the logs, or when troubleshooting
LDAP Group Sync, this error may indicate a configuration problem. The LDAP
configuration `/etc/gitlab/gitlab.rb` (Omnibus) or `config/gitlab.yml` (source)
......@@ -202,3 +239,178 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
account control attribute (`userAccountControl:1.2.840.113556.1.4.803`)
has bit 2 set. See https://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
for more information.
### User DN has changed
When an LDAP user is created in GitLab, their LDAP DN is stored for later reference.
If a user's DN changes, it can cause problems for LDAP sync. Administrators can
manually update a user's stored DN in this case.
> **Note:** If GitLab cannot find a user by their DN, it will attempt to fallback
to finding the user by their email. If the lookup is successful, GitLab will
update the stored DN to the new value.
1. Sign in to GitLab as an administrator user.
1. Navigate to **Admin area -> Users**.
1. Search for the user
1. Open the user, by clicking on their name. Do not click 'Edit'.
1. Navigate to the **Identities** tab.
1. Click 'Edit' next to the LDAP identity.
1. Change the 'Identifier' to match the user's new LDAP DN.
1. Save the identity.
Now the user should sync correctly.
### User is not being added to a group
Sometimes you may think a particular user should be added to a GitLab group via
LDAP group sync, but for some reason it's not happening. There are several
things to check to debug the situation.
- Ensure LDAP configuration has a `group_base` specified. This configuration is
required for group sync to work properly.
- Ensure the correct LDAP group link is added to the GitLab group. Check group
links by visiting the GitLab group, then **Settings dropdown -> LDAP groups**.
- Check that the user has an LDAP identity
1. Sign in to GitLab as an administrator user.
1. Navigate to **Admin area -> Users**.
1. Search for the user
1. Open the user, by clicking on their name. Do not click 'Edit'.
1. Navigate to the **Identities** tab. There should be an LDAP identity with
an LDAP DN as the 'Identifier'.
If all of the above looks good, jump in to a little more advanced debugging.
Often, the best way to learn more about why group sync is behaving a certain
way is to enable debug logging. There is verbose output that details every
step of the sync.
1. Start a Rails console
```bash
# For Omnibus installations
sudo gitlab-rails console
# For installations from source
sudo -u git -H bundle exec rails console production
```
1. Set the log level to debug (only for this session):
```ruby
Rails.logger.level = Logger::DEBUG
```
1. Choose a GitLab group to test with. This group should have an LDAP group link
already configured. If the output is `nil`, the group could not be found.
If a bunch of group attributes are output, your group was found successfully.
```ruby
group = Group.find_by(name: 'my_group')
# Output
=> #<Group:0x007fe825196558 id: 1234, name: "my_group"...>
```
1. Run a group sync for this particular group.
```ruby
EE::Gitlab::LDAP::Sync::Group.execute_all_providers(group)
```
1. Look through the output of the sync. See [example log output](#example-log-output)
below for more information about the output.
1. If you still aren't able to see why the user isn't being added, query the
LDAP group directly to see what members are listed. Still in the Rails console,
run the following query:
```ruby
adapter = Gitlab::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider
ldap_group = EE::Gitlab::LDAP::Group.find_by_cn('group_cn_here', adapter)
# Output
=> #<EE::Gitlab::LDAP::Group:0x007fcbdd0bb6d8
```
1. Query the LDAP group's member DNs and see if the user's DN is in the list.
One of the DNs here should match the 'Identifier' from the LDAP identity
checked earlier. If it doesn't, the user does not appear to be in the LDAP
group.
```ruby
ldap_group.member_dns
# Output
=> ["uid=john,ou=people,dc=example,dc=com", "uid=mary,ou=people,dc=example,dc=com"]
```
1. Some LDAP servers don't store members by DN. Rather, they use UIDs instead.
If you didn't see results from the last query, try querying by UIDs instead.
```ruby
ldap_group.member_uids
# Output
=> ['john','mary']
```
#### Example log output
The output of the last command will be very verbose, but contains lots of
helpful information. For the most part you can ignore log entries that are SQL
statements.
Indicates the point where syncing actually begins:
```bash
Started syncing all providers for 'my_group' group
```
The follow entry shows an array of all user DNs GitLab sees in the LDAP server.
Note that these are the users for a single LDAP group, not a GitLab group. If
you have multiple LDAP groups linked to this GitLab group, you will see multiple
log entries like this - one for each LDAP group. If you don't see an LDAP user
DN in this log entry, LDAP is not returning the user when we do the lookup.
Verify the user is actually in the LDAP group.
```bash
Members in 'ldap_group_1' LDAP group: ["uid=john0,ou=people,dc=example,dc=com",
"uid=mary0,ou=people,dc=example,dc=com", "uid=john1,ou=people,dc=example,dc=com",
"uid=mary1,ou=people,dc=example,dc=com", "uid=john2,ou=people,dc=example,dc=com",
"uid=mary2,ou=people,dc=example,dc=com", "uid=john3,ou=people,dc=example,dc=com",
"uid=mary3,ou=people,dc=example,dc=com", "uid=john4,ou=people,dc=example,dc=com",
"uid=mary4,ou=people,dc=example,dc=com"]
```
Shortly after each of the above entries, you will see a hash of resolved member
access levels. This hash represents all user DNs GitLab thinks should have
access to this group, and at which access level (role). This hash is additive,
and more DNs may be added, or existing entries modified, based on additional
LDAP group lookups. The very last occurrence of this entry should indicate
exactly which users GitLab believes should be added to the group.
> **Note:** 10 is 'Guest', 20 is 'Reporter', 30 is 'Developer', 40 is 'Master'
and 50 is 'Owner'
```bash
Resolved 'my_group' group member access: {"uid=john0,ou=people,dc=example,dc=com"=>30,
"uid=mary0,ou=people,dc=example,dc=com"=>30, "uid=john1,ou=people,dc=example,dc=com"=>30,
"uid=mary1,ou=people,dc=example,dc=com"=>30, "uid=john2,ou=people,dc=example,dc=com"=>30,
"uid=mary2,ou=people,dc=example,dc=com"=>30, "uid=john3,ou=people,dc=example,dc=com"=>30,
"uid=mary3,ou=people,dc=example,dc=com"=>30, "uid=john4,ou=people,dc=example,dc=com"=>30,
"uid=mary4,ou=people,dc=example,dc=com"=>30}
```
It's not uncommon to see warnings like the following. These indicate that GitLab
would have added the user to a group, but the user could not be found in GitLab.
Usually this is not a cause for concern.
If you think a particular user should already exist in GitLab, but you're seeing
this entry, it could be due to a mismatched DN stored in GitLab. See
[User DN has changed](#User-DN-has-changed) to update the user's LDAP identity.
```bash
User with DN `uid=john0,ou=people,dc=example,dc=com` should have access
to 'my_group' group but there is no user in GitLab with that
identity. Membership will be updated once the user signs in for
the first time.
```
Finally, the following entry says syncing has finished for this group:
```bash
Finished syncing all providers for 'my_group' group
```
......@@ -11,6 +11,10 @@ membership syncing.
The information on this page is relevent for both GitLab CE and EE. For more
details about EE-specific LDAP features, see [LDAP EE Documentation](ldap-ee.md).
[//]: # (Do *NOT* modify this file in EE documentation. All changes in this)
[//]: # (file should happen in CE, too. If the change is EE-specific, put)
[//]: # (it in `ldap-ee.md`.)
## Security
GitLab assumes that LDAP users are not able to change their LDAP 'mail', 'email'
......@@ -206,28 +210,6 @@ production:
# snip...
```
### External Groups
>**Note:** External Groups configuration is only available in GitLab EE Version
8.9 and above.
Using the `external_groups` setting will allow you to mark all users belonging
to these groups as [external users](../../user/permissions.md). Group membership is
checked periodically through the `LdapGroupSync` background task.
**Configuration**
```yaml
# An array of CNs of groups containing users that should be considered external
#
# Ex. ['interns', 'contractors']
#
# Note: Not `cn=interns` or the full DN
#
external_groups: []
```
## Using an LDAP filter to limit access to your GitLab server
If you want to limit all GitLab access to a subset of the LDAP users on your
......@@ -278,6 +260,23 @@ In other words, if an existing GitLab user wants to enable LDAP sign-in for
themselves, they should check that their GitLab email address matches their
LDAP email address, and then sign into GitLab via their LDAP credentials.
## Limitations
### TLS Client Authentication
Not implemented by `Net::LDAP`.
You should disable anonymous LDAP authentication and enable simple or SASL
authentication. The TLS client authentication setting in your LDAP server cannot
be mandatory and clients cannot be authenticated with the TLS protocol.
### TLS Server Authentication
Not supported by GitLab's configuration options.
When setting `method: ssl`, the underlying authentication method used by
`omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with
the LDAP server before any LDAP-protocol data is exchanged but no validation of
the LDAP server's SSL certificate is performed.
## Troubleshooting
### Debug LDAP user filter with ldapsearch
......@@ -287,7 +286,7 @@ following query returns the login names of the users that will be allowed to
log in to GitLab if you configure your own user_filter.
```
ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$base" "(&(ObjectClass=User)($user_filter))" sAMAccountName
ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$base" "$user_filter" sAMAccountName
```
- Variables beginning with a `$` refer to a variable from the LDAP section of
......@@ -297,6 +296,7 @@ ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$ba
port.
- We are assuming the password for the bind_dn user is in bind_dn_password.txt.
### Invalid credentials when logging in
- Make sure the user you are binding with has enough permissions to read the user's
......
......@@ -7,19 +7,10 @@ highly available.
## Architecture
### Active/Passive
For pure high-availability/failover with no scaling you can use an
active/passive configuration. This utilizes DRBD (Distributed Replicated
Block Device) to keep all data in sync. DRBD requires a low latency link to
remain in sync. It is not advisable to attempt to run DRBD between data centers
or in different cloud availability zones.
There are two kinds of setups:
Components/Servers Required:
- 2 servers/virtual machines (one active/one passive)
![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png)
- active/active
- active/passive
### Active/Active
......@@ -28,12 +19,24 @@ user requests simultaneously. The database, Redis, and GitLab application are
all deployed on separate servers. The configuration is **only** highly-available
if the database, Redis and storage are also configured as such.
![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png)
**Steps to configure active/active:**
Follow the steps below to configure an active/active setup:
1. [Configure the database](database.md)
1. [Configure Redis](redis.md)
1. [Configure NFS](nfs.md)
1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md)
![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png)
### Active/Passive
For pure high-availability/failover with no scaling you can use an
active/passive configuration. This utilizes DRBD (Distributed Replicated
Block Device) to keep all data in sync. DRBD requires a low latency link to
remain in sync. It is not advisable to attempt to run DRBD between data centers
or in different cloud availability zones.
Components/Servers Required: 2 servers/virtual machines (one active/one passive)
![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png)
This diff is collapsed.
......@@ -43,18 +43,14 @@ Keep in mind that:
## Setup instructions
GitLab Geo requires some additional work installing and configuring your
instance, than a normal setup.
There are a couple of things you need to do in order to have one or more GitLab
Geo instances. Follow the steps below in the **exact order** that they appear:
1. Follow the instructions to [install GitLab Enterprise Edition][install-ee]
on the server that will serve as the secondary Geo node, but don't further
configure GitLab as authentication will be handled by the primary node (more
on this in the configuration step).
1. [Setup a database replication](database.md) in `primary <-> secondary (read-only)` topology.
1. [Configure GitLab](configuration.md) and set the primary and secondary nodes.
In order to set up one or more GitLab Geo instances, follow the steps below in
this **exact order**:
1. Follow the first 3 steps to [install GitLab Enterprise Edition][install-ee]
on the server that will serve as the secondary Geo node. Do not login or
set up anything else in the secondary node for the moment.
1. [Setup the database replication](database.md) (`primary <-> secondary (read-only)` topology)
1. [Configure GitLab](configuration.md) to set the primary and secondary nodes.
## After setup
......
This diff is collapsed.
This diff is collapsed.
# Geo nodes admin area
For more information about setting up GitLab Geo, read the
[Geo documentation](../../gitlab-geo/README.md).
When you're done, you can navigate to **Admin area ➔ Geo nodes** (`/admin/geo_nodes`).
In the following table you can see what all these settings mean:
| Setting | Description |
| --------- | ----------- |
| Primary | This marks a Geo Node as primary. There can be only one primary, make sure that you first add the primary node and then all the others. |
| URL | Your instance's full URL, in the same way it is configured in `/etc/gitlab/gitlab.rb` (Omnibus GitLab installations) or `gitlab.yml` (source based installations). |
| Public Key | The SSH public key of the user that your GitLab instance runs on (unless changed, should be the user `git`). |
A primary node will have a star right next to it to distinguish from the
secondaries.
module EE
module Workers
# Geo specific code for cache re-generation
#
# This module is intended to encapsulate EE-specific methods
# and be **prepended** in the `ProjectCacheWorker` class.
module ProjectCacheWorker
def update_caches(project_id)
if ::Gitlab::Geo.secondary?
update_geo_caches(project_id)
else
super
end
end
private
# Geo should only update Redis based cache, as data store in the database
# will be updated on primary and replicated to the secondaries.
def update_geo_caches(project_id)
project = Project.find(project_id)
return unless project.repository.exists?
if project.repository.root_ref
project.repository.build_cache
end
end
end
end
end
require 'spec_helper'
describe Geo::ScheduleRepoUpdateService, services: true do
include RepoHelpers
let(:user) { create :user }
let(:project) { create :project }
let(:blankrev) { Gitlab::Git::BLANK_SHA }
let(:oldrev) { sample_commit.parent_id }
let(:newrev) { sample_commit.id }
let(:ref) { 'refs/heads/master' }
let(:service) { execute_push_service(project, user, oldrev, newrev, ref) }
before do
project.team << [user, :master]
end
subject { described_class.new(service.push_data) }
context 'parsed push_data' do
it 'includes required params' do
expect(subject.push_data).to include('type', 'before', 'after', 'ref')
end
end
context '#execute' do
let(:push_data) { service.push_data }
let(:args) do
[
project.id,
push_data[:project][:git_ssh_url],
{
'type' => push_data[:object_kind],
'before' => push_data[:before],
'after' => push_data[:newref],
'ref' => push_data[:ref]
}
]
end
it 'schedule update service' do
expect(GeoRepositoryUpdateWorker).to receive(:perform_async).with(*args)
subject.execute
end
end
def execute_push_service(project, user, oldrev, newrev, ref)
service = GitPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
service.execute
service
end
end
......@@ -22,5 +22,13 @@ describe Users::ActivityService, services: true do
end
end
end
context 'when in Geo secondary node' do
before { allow(Gitlab::Geo).to receive(:secondary?).and_return(true) }
it 'does not update last_activity_at' do
expect { service.execute }.not_to change { user.reload.last_activity_at }
end
end
end
end
require 'spec_helper'
describe GeoRepositoryUpdateWorker do
include RepoHelpers
let(:user) { create :user }
let(:project) { create :project }
let(:blankrev) { Gitlab::Git::BLANK_SHA }
let(:oldrev) { sample_commit.parent_id }
let(:newrev) { sample_commit.id }
let(:ref) { 'refs/heads/master' }
let(:service) { execute_push_service(project, user, oldrev, newrev, ref) }
let(:push_data) { service.push_data }
let(:parsed_push_data) do
{
'type' => push_data[:object_kind],
'before' => push_data[:before],
'after' => push_data[:after],
'ref' => push_data[:ref]
}
end
let(:clone_url) { push_data[:project][:git_ssh_url] }
let(:performed) { subject.perform(project.id, clone_url, parsed_push_data) }
before do
project.team << [user, :master]
expect(Project).to receive(:find).at_least(:once).with(project.id) { project }
end
context 'when no repository' do
before do
allow(project.repository).to receive(:fetch_geo_mirror)
allow(project).to receive(:repository_exists?) { false }
end
it 'creates a new repository' do
expect(project).to receive(:create_repository)
performed
end
it 'executes after_create hook' do
expect(project.repository).to receive(:after_create)
performed
end
end
context 'when empty repository' do
before do
allow(project.repository).to receive(:fetch_geo_mirror)
allow(project).to receive(:empty_repo?) { true }
end
it 'executes after_create hook' do
expect(project.repository).to receive(:after_create).at_least(:once)
performed
end
end
context '#process_hooks' do
before { allow(subject).to receive(:fetch_repository) }
it 'calls if push_data is present' do
expect(subject).to receive(:process_hooks)
performed
end
context 'when no push_data is present' do
let(:parsed_push_data) { nil }
it 'skips process_hooks' do
expect(subject).not_to receive(:process_hooks)
performed
end
end
end
context '#process_push' do
before { allow(subject).to receive(:fetch_repository) }
it 'executes after_push_commit' do
expect(project.repository).to receive(:after_push_commit).at_least(:once).with('master', newrev)
performed
end
context 'when removing branch' do
it 'executes after_remove_branch' do
allow(subject).to receive(:push_remove_branch?) { true }
expect(project.repository).to receive(:after_remove_branch)
performed
end
end
context 'when updating a new branch' do
it 'executes after_create_branch' do
allow(subject).to receive(:push_to_new_branch?) { true }
expect(project.repository).to receive(:after_create_branch)
performed
end
end
end
def execute_push_service(project, user, oldrev, newrev, ref)
service = GitPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
service.execute
service
end
end
......@@ -39,6 +39,8 @@ describe ProjectCacheWorker do
expect_any_instance_of(Project).to receive(:update_repository_size)
expect_any_instance_of(Project).to receive(:update_commit_count)
expect_any_instance_of(Repository).to receive(:build_cache).and_call_original
subject.perform(project.id)
end
......@@ -48,6 +50,21 @@ describe ProjectCacheWorker do
subject.perform(project.id)
end
context 'when in Geo secondary node' do
before do
allow(Gitlab::Geo).to receive(:secondary?) { true }
end
it 'updates only non database cache' do
expect_any_instance_of(Repository).to receive(:build_cache).and_call_original
expect_any_instance_of(Project).not_to receive(:update_repository_size)
expect_any_instance_of(Project).not_to receive(:update_commit_count)
subject.perform(project.id)
end
end
end
context 'when an exclusive lease can not be obtained' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment