diff options
-rw-r--r-- | app/assets/javascripts/dispatcher.js.es6 | 2 | ||||
-rw-r--r-- | app/assets/javascripts/issues_bulk_assignment.js.es6 | 14 | ||||
-rw-r--r-- | app/assets/javascripts/labels_select.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/render_math.js | 2 | ||||
-rw-r--r-- | app/helpers/groups_helper.rb | 11 | ||||
-rw-r--r-- | app/helpers/projects_helper.rb | 4 | ||||
-rw-r--r-- | app/models/group.rb | 2 | ||||
-rw-r--r-- | app/models/namespace.rb | 13 | ||||
-rw-r--r-- | app/views/admin/groups/_group.html.haml | 2 | ||||
-rw-r--r-- | app/views/admin/groups/show.html.haml | 2 | ||||
-rw-r--r-- | app/views/shared/groups/_group.html.haml | 2 | ||||
-rw-r--r-- | doc/administration/auth/README.md | 3 | ||||
-rw-r--r-- | doc/administration/auth/img/okta_admin_panel.png | bin | 0 -> 26164 bytes | |||
-rw-r--r-- | doc/administration/auth/img/okta_saml_settings.png | bin | 0 -> 25470 bytes | |||
-rw-r--r-- | doc/administration/auth/okta.md | 160 | ||||
-rw-r--r-- | lib/gitlab/asciidoc.rb | 4 | ||||
-rw-r--r-- | spec/features/admin/admin_active_tab_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/namespace_spec.rb | 22 |
18 files changed, 223 insertions, 24 deletions
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 1ec950494ff..1e259a16f06 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -75,7 +75,7 @@ case 'projects:issues:index': Issuable.init(); new gl.IssuableBulkActions({ - prefixId: page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_' + prefixId: page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_', }); shortcut_handler = new ShortcutsNavigation(); break; diff --git a/app/assets/javascripts/issues_bulk_assignment.js.es6 b/app/assets/javascripts/issues_bulk_assignment.js.es6 index 1c8e5dede6f..52fd5d71b18 100644 --- a/app/assets/javascripts/issues_bulk_assignment.js.es6 +++ b/app/assets/javascripts/issues_bulk_assignment.js.es6 @@ -6,7 +6,7 @@ class IssuableBulkActions { constructor({ container, form, issues, prefixId } = {}) { - this.prefixId = prefixId || 'issue_'; + this.prefixId = prefixId || 'issue_'; this.form = form || this.getElement('.bulk-update'); this.$labelDropdown = this.form.find('.js-label-select'); this.issues = issues || this.getElement('.issues-list .issue'); @@ -107,7 +107,7 @@ } setOriginalDropdownData() { - let $labelSelect = $('.bulk-update .js-label-select'); + const $labelSelect = $('.bulk-update .js-label-select'); $labelSelect.data('common', this.getOriginalCommonIds()); $labelSelect.data('marked', this.getOriginalMarkedIds()); $labelSelect.data('indeterminate', this.getOriginalIndeterminateIds()); @@ -115,7 +115,7 @@ // From issuable's initial bulk selection getOriginalCommonIds() { - let labelIds = []; + const labelIds = []; this.getElement('.selected_issue:checked').each((i, el) => { labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); @@ -125,17 +125,17 @@ // From issuable's initial bulk selection getOriginalMarkedIds() { - var labelIds = []; + const labelIds = []; this.getElement('.selected_issue:checked').each((i, el) => { labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); }); - return _.intersection.apply(_, labelIds); + return _.intersection.apply(this, labelIds); } // From issuable's initial bulk selection getOriginalIndeterminateIds() { - let uniqueIds = []; - let labelIds = []; + const uniqueIds = []; + const labelIds = []; let issuableLabels = []; // Collect unique label IDs for all checked issues diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 6853d6b9db2..ec2fc87bece 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -8,7 +8,7 @@ var _this; _this = this; $('.js-label-select').each(function(i, dropdown) { - var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container; + var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer; $dropdown = $(dropdown); $dropdownContainer = $dropdown.closest('.labels-filter'); $toggleText = $dropdown.find('.dropdown-toggle-text'); diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js index a8a56430f88..209e7a8661d 100644 --- a/app/assets/javascripts/render_math.js +++ b/app/assets/javascripts/render_math.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */ +/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len, no-console */ // Renders math using KaTeX in any element with the // `js-render-math` class // diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index f6d4ea4659a..77dc9e7d538 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -12,11 +12,18 @@ module GroupsHelper end def group_title(group, name = nil, url = nil) - full_title = link_to(simple_sanitize(group.name), group_path(group)) + full_title = '' + + group.parents.each do |parent| + full_title += link_to(simple_sanitize(parent.name), group_path(parent)) + full_title += ' / '.html_safe + end + + full_title += link_to(simple_sanitize(group.name), group_path(group)) full_title += ' · '.html_safe + link_to(simple_sanitize(name), url) if name content_tag :span do - full_title + full_title.html_safe end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9cda3b78761..d2177f683a1 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -52,7 +52,7 @@ module ProjectsHelper def project_title(project) namespace_link = if project.group - link_to(simple_sanitize(project.group.name), group_path(project.group)) + group_title(project.group) else owner = project.namespace.owner link_to(simple_sanitize(owner.name), user_path(owner)) @@ -390,7 +390,7 @@ module ProjectsHelper "success" end end - + def readme_cache_key sha = @project.commit.try(:sha) || 'nil' [@project.path_with_namespace, sha, "readme"].join('-') diff --git a/app/models/group.rb b/app/models/group.rb index 4248e1162d8..ac8a82c8c1e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -83,7 +83,7 @@ class Group < Namespace end def human_name - name + full_name end def visibility_level_field diff --git a/app/models/namespace.rb b/app/models/namespace.rb index f0479d94986..fd42f2328d8 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -161,6 +161,19 @@ class Namespace < ActiveRecord::Base end end + def full_name + @full_name ||= + if parent + parent.full_name + ' / ' + name + else + name + end + end + + def parents + @parents ||= parent ? parent.parents + [parent] : [] + end + private def repository_storage_paths diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index 4efeec0ea4e..cf28f92853e 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -20,7 +20,7 @@ = image_tag group_icon(group), class: "avatar s40 hidden-xs" .title = link_to [:admin, group], class: 'group-name' do - = group.name + = group.full_name - if group.description.present? .description diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 71a605f33b1..7b0175af214 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -1,6 +1,6 @@ - page_title @group.name, "Groups" %h3.page-title - Group: #{@group.name} + Group: #{@group.full_name} = link_to admin_group_edit_path(@group), class: "btn pull-right" do %i.fa.fa-pencil-square-o diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 19221e3391f..8164f61797c 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -28,7 +28,7 @@ = image_tag group_icon(group), class: "avatar s40 hidden-xs" .title = link_to group, class: 'group-name' do - = group.name + = group.full_name - if group_member as diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md index 07e548aaabe..2fc5d0355b5 100644 --- a/doc/administration/auth/README.md +++ b/doc/administration/auth/README.md @@ -7,5 +7,6 @@ providers. and 389 Server - [OmniAuth](../../integration/omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, Crowd and Azure -- [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider - [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS +- [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider +- [Okta](okta.md) Configure GitLab to sign in using Okta diff --git a/doc/administration/auth/img/okta_admin_panel.png b/doc/administration/auth/img/okta_admin_panel.png Binary files differnew file mode 100644 index 00000000000..12e21956715 --- /dev/null +++ b/doc/administration/auth/img/okta_admin_panel.png diff --git a/doc/administration/auth/img/okta_saml_settings.png b/doc/administration/auth/img/okta_saml_settings.png Binary files differnew file mode 100644 index 00000000000..ee275ece369 --- /dev/null +++ b/doc/administration/auth/img/okta_saml_settings.png diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md new file mode 100644 index 00000000000..cb42b7743c5 --- /dev/null +++ b/doc/administration/auth/okta.md @@ -0,0 +1,160 @@ +# Okta SSO provider + +Okta is a [Single Sign-on provider][okta-sso] that can be used to authenticate +with GitLab. + +The following documentation enables Okta as a SAML provider. + +## Configure the Okta application + +1. On Okta go to the admin section and choose to **Add an App**. +1. When the app screen comes up you see another button to **Create an App** and + choose SAML 2.0 on the next screen. +1. Now, very important, add a logo + (you can choose it from https://about.gitlab.com/press/). You'll have to + crop and resize it. +1. Next, you'll need the to fill in the SAML general config. Here's an example + image. + + ![Okta admin panel view](img/okta_admin_panel.png) + +1. The last part of the configuration is the feedback section where you can + just say you're a customer and creating an app for internal use. +1. When you have your app you'll have a few tabs on the top of the app's + profile. Click on the SAML 2.0 config instructions button which should + look like the following: + + ![Okta SAML settings](img/okta_saml_settings.png) + +1. On the screen that comes up take note of the + **Identity Provider Single Sign-On URL** which you'll use for the + `idp_sso_target_url` on your GitLab config file. + +1. **Before you leave Okta make sure you add your user and groups if any.** + +--- + +Now that the Okta app is configured, it's time to enable it in GitLab. + +## Configure GitLab + +1. On your GitLab server, open the configuration file: + + **For Omnibus GitLab installations** + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + **For installations from source** + + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) + for initial settings. + +1. To allow your users to use Okta to sign up without having to manually create + an account first, don't forget to add the following values to your + configuration: + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] + gitlab_rails['omniauth_block_auto_created_users'] = false + ``` + + **For installations from source** + + ```yaml + allow_single_sign_on: ["saml"] + block_auto_created_users: false + ``` + +1. You can also automatically link Okta users with existing GitLab users if + their email addresses match by adding the following setting: + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_auto_link_saml_user'] = true + ``` + + **For installations from source** + + ```yaml + auto_link_saml_user: true + ``` + +1. Add the provider configuration. + + >**Notes:** + >- Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint + of GitLab (append `users/auth/saml/callback` to the HTTPS URL of your GitLab + installation to generate the correct value). + >- To get the `idp_cert_fingerprint` fingerprint, first download the + certificate from the Okta app you registered and then run: + `openssl x509 -in okta.cert -noout -fingerprint`. Substitute `okta.cert` + with the location of your certificate. + >- Change the value of `idp_sso_target_url`, with the value of the + **Identity Provider Single Sign-On URL** from the step when you + configured the Okta app. + >- Change the value of `issuer` to a unique name, which will identify the application + to the IdP. + >- Leave `name_identifier_format` as-is. + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }, + label: 'Okta' # optional label for SAML login button, defaults to "Saml" + } + ] + ``` + + **For installations from source** + + ```yaml + - { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }, + label: 'Okta' # optional label for SAML login button, defaults to "Saml" + } + ``` + + +1. [Reconfigure][reconf] or [restart] GitLab for Omnibus and installations + from source respectively for the changes to take effect. + +You might want to try this out on a incognito browser window. + +## Configuring groups + +>**Note:** +Make sure the groups exist and are assigned to the Okta app. + +You can take a look of the [SAML documentation][saml] on external groups since +it works the same. + +[okta-sso]: https://www.okta.com/products/single-sign-on/ +[saml]: ../../integration/saml.md#external-groups +[reconf]: ../restart_gitlab.md#omnibus-gitlab-reconfigure +[restart]: ../restart_gitlab.md#installations-from-source diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index f77f412da56..fa234284361 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -35,11 +35,7 @@ module Gitlab html.html_safe end - end -end -module Gitlab - module Asciidoc class Html5Converter < Asciidoctor::Converter::Html5Converter extend Asciidoctor::Converter::Config diff --git a/spec/features/admin/admin_active_tab_spec.rb b/spec/features/admin/admin_active_tab_spec.rb index f2eecc5b552..16064d60ce2 100644 --- a/spec/features/admin/admin_active_tab_spec.rb +++ b/spec/features/admin/admin_active_tab_spec.rb @@ -29,7 +29,7 @@ RSpec.describe 'admin active tab' do context 'on projects' do before do - visit admin_namespaces_projects_path + visit admin_projects_path end it_behaves_like 'page has active tab', 'Overview' diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 069c59fb5ca..9fd06bb6b23 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -128,4 +128,26 @@ describe Namespace, models: true do it { expect(group.full_path).to eq(group.path) } it { expect(nested_group.full_path).to eq("#{group.path}/#{nested_group.path}") } end + + describe '#full_name' do + let(:group) { create(:group) } + let(:nested_group) { create(:group, parent: group) } + + it { expect(group.full_name).to eq(group.name) } + it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") } + end + + describe '#parents' do + let(:group) { create(:group) } + let(:nested_group) { create(:group, parent: group) } + let(:deep_nested_group) { create(:group, parent: nested_group) } + let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) } + + it 'returns the correct parents' do + expect(very_deep_nested_group.parents).to eq([group, nested_group, deep_nested_group]) + expect(deep_nested_group.parents).to eq([group, nested_group]) + expect(nested_group.parents).to eq([group]) + expect(group.parents).to eq([]) + end + end end |