Commit 97d7694e authored by James Edwards-Jones's avatar James Edwards-Jones

Settings page for Per Group SAML

SamlProvider defaults to enabled, validates fields, can create/update, displays info to user
SamlProvider requires top level group
License and policy checks for SamlProvidersController
parent f35c2bf6
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
border-bottom: 1px solid $well-inner-border; border-bottom: 1px solid $well-inner-border;
} }
&.borderless {
border-bottom: 0;
}
&.branch-info { &.branch-info {
.commit-sha, .commit-sha,
.commit-info { .commit-info {
......
...@@ -297,6 +297,12 @@ ...@@ -297,6 +297,12 @@
} }
} }
.saml-settings.info-well {
.form-control[readonly] {
background: $white-light;
}
}
.modal-doorkeepr-auth, .modal-doorkeepr-auth,
.doorkeeper-app-form { .doorkeeper-app-form {
.scope-description { .scope-description {
......
...@@ -638,6 +638,8 @@ production: &base ...@@ -638,6 +638,8 @@ production: &base
# name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' # name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
# } } # } }
# #
# - { name: 'group_saml' }
#
# - { name: 'crowd', # - { name: 'crowd',
# args: { # args: {
# crowd_server_url: 'CROWD SERVER URL', # crowd_server_url: 'CROWD SERVER URL',
......
...@@ -26,5 +26,6 @@ end ...@@ -26,5 +26,6 @@ end
module OmniAuth module OmniAuth
module Strategies module Strategies
autoload :Bitbucket, Rails.root.join('lib', 'omni_auth', 'strategies', 'bitbucket') autoload :Bitbucket, Rails.root.join('lib', 'omni_auth', 'strategies', 'bitbucket')
autoload :GroupSaml, Rails.root.join('ee', 'lib', 'omni_auth', 'strategies', 'group_saml')
end end
end end
...@@ -67,6 +67,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -67,6 +67,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resources :ldap_group_links, only: [:index, :create, :destroy] resources :ldap_group_links, only: [:index, :create, :destroy]
resource :saml_providers, path: 'saml', only: [:show, :create, :update] do
get :sso, to: 'sso#saml'
end
resource :notification_setting, only: [:update] resource :notification_setting, only: [:update]
resources :audit_events, only: [:index] resources :audit_events, only: [:index]
resources :pipeline_quota, only: [:index] resources :pipeline_quota, only: [:index]
......
...@@ -2202,6 +2202,15 @@ ActiveRecord::Schema.define(version: 20180327101207) do ...@@ -2202,6 +2202,15 @@ ActiveRecord::Schema.define(version: 20180327101207) do
add_index "routes", ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"} add_index "routes", ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
create_table "saml_providers", force: :cascade do |t|
t.integer "group_id", null: false
t.boolean "enabled", null: false
t.string "certificate_fingerprint", null: false
t.string "sso_url", null: false
end
add_index "saml_providers", ["group_id"], name: "index_saml_providers_on_group_id", using: :btree
create_table "sent_notifications", force: :cascade do |t| create_table "sent_notifications", force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
t.integer "noteable_id" t.integer "noteable_id"
...@@ -2779,6 +2788,7 @@ ActiveRecord::Schema.define(version: 20180327101207) do ...@@ -2779,6 +2788,7 @@ ActiveRecord::Schema.define(version: 20180327101207) do
add_foreign_key "push_rules", "projects", name: "fk_83b29894de", on_delete: :cascade add_foreign_key "push_rules", "projects", name: "fk_83b29894de", on_delete: :cascade
add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade
add_foreign_key "remote_mirrors", "projects", name: "fk_43a9aa4ca8", on_delete: :cascade add_foreign_key "remote_mirrors", "projects", name: "fk_43a9aa4ca8", on_delete: :cascade
add_foreign_key "saml_providers", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade
add_foreign_key "slack_integrations", "services", on_delete: :cascade add_foreign_key "slack_integrations", "services", on_delete: :cascade
add_foreign_key "snippets", "projects", name: "fk_be41fd4bb7", on_delete: :cascade add_foreign_key "snippets", "projects", name: "fk_be41fd4bb7", on_delete: :cascade
......
class Groups::SamlProvidersController < Groups::ApplicationController
before_action :require_top_level_group
before_action :authorize_manage_saml!
before_action :check_group_saml_available!
before_action :check_group_saml_configured
before_action :check_group_saml_beta_enabled
def show
@saml_provider = @group.saml_provider || @group.build_saml_provider
end
def create
@saml_provider = @group.build_saml_provider(saml_provider_params)
@saml_provider.save
render :show
end
def update
@saml_provider = @group.saml_provider
@saml_provider.update(saml_provider_params)
render :show
end
private
def authorize_manage_saml!
render_404 unless can?(current_user, :admin_group_saml, @group)
end
def check_group_saml_configured
render_404 unless Gitlab::Auth::GroupSaml::Config.enabled?
end
def check_group_saml_beta_enabled
render_404 unless Gitlab::Utils.to_boolean(cookies['enable_group_saml'])
end
def require_top_level_group
render_404 if @group.subgroup?
end
def saml_provider_params
allowed_params = %i[sso_url certificate_fingerprint enabled]
params.require(:saml_provider).permit(allowed_params)
end
end
...@@ -5,7 +5,7 @@ module EE ...@@ -5,7 +5,7 @@ module EE
override :group_nav_link_paths override :group_nav_link_paths
def group_nav_link_paths def group_nav_link_paths
if ::Gitlab::CurrentSettings.should_check_namespace_plan? && can?(current_user, :admin_group, @group) if ::Gitlab::CurrentSettings.should_check_namespace_plan? && can?(current_user, :admin_group, @group)
super + %w[billings#index] super + %w[billings#index saml_providers#show]
else else
super super
end end
......
module EE
module SamlProvidersHelper
def group_saml_enabled?
group_saml_beta_enabled? && ::Gitlab::Auth::GroupSaml::Config.enabled?
end
def group_saml_beta_enabled?
::Gitlab::Utils.to_boolean(cookies['enable_group_saml'])
end
def show_saml_in_sidebar?(group)
group_saml_enabled? && !group.subgroup? && can?(current_user, :admin_group_saml, group)
end
def saml_link(text, group_id, redirect: nil, html_class: 'btn')
redirect ||= group_path(group_id)
url = omniauth_authorize_path(:user, :group_saml, group_id: group_id, redirect_to: redirect)
link_to(text, url, method: :post, class: html_class)
end
end
end
...@@ -10,6 +10,8 @@ module EE ...@@ -10,6 +10,8 @@ module EE
included do included do
has_many :epics has_many :epics
has_one :saml_provider
state_machine :ldap_sync_status, namespace: :ldap_sync, initial: :ready do state_machine :ldap_sync_status, namespace: :ldap_sync, initial: :ready do
state :ready state :ready
state :started state :started
......
...@@ -50,6 +50,7 @@ class License < ActiveRecord::Base ...@@ -50,6 +50,7 @@ class License < ActiveRecord::Base
multiple_group_issue_boards multiple_group_issue_boards
merge_request_performance_metrics merge_request_performance_metrics
object_storage object_storage
group_saml
service_desk service_desk
variable_environment_scope variable_environment_scope
reject_unsigned_commits reject_unsigned_commits
......
class SamlProvider < ActiveRecord::Base
belongs_to :group
validates :group, presence: true, top_level_group: true
validates :sso_url, presence: true, url: { protocols: %w(https) }
validates :certificate_fingerprint, presence: true, certificate_fingerprint: true
after_initialize :set_defaults, if: :new_record?
NAME_IDENTIFIER_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'.freeze
def assertion_consumer_service_url
"#{full_group_path}/-/saml/callback"
end
def issuer
full_group_path
end
def name_identifier_format
NAME_IDENTIFIER_FORMAT
end
def settings
{
assertion_consumer_service_url: assertion_consumer_service_url,
issuer: issuer,
idp_cert_fingerprint: certificate_fingerprint,
idp_sso_target_url: sso_url,
name_identifier_format: name_identifier_format
}
end
private
def full_group_path
"#{host}/groups/#{group.full_path}"
end
def set_defaults
self.enabled = true
end
def host
@host ||= Gitlab.config.gitlab.url
end
end
...@@ -49,6 +49,8 @@ module EE ...@@ -49,6 +49,8 @@ module EE
rule { admin }.enable :read_epic rule { admin }.enable :read_epic
rule { has_projects }.enable :read_epic rule { has_projects }.enable :read_epic
rule { admin | owner }.enable :admin_group_saml
rule { admin | (can_owners_manage_ldap & owner) }.enable :admin_ldap_group_links rule { admin | (can_owners_manage_ldap & owner) }.enable :admin_ldap_group_links
rule { ldap_synced }.prevent :admin_group_member rule { ldap_synced }.prevent :admin_group_member
......
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
%span %span
LDAP Synchronization LDAP Synchronization
- if show_saml_in_sidebar?(@group)
= nav_link(path: 'saml_providers#show') do
= link_to group_saml_providers_path(@group), title: 'SAML SSO' do
%span
SAML SSO
- if @group.feature_available?(:group_webhooks) || show_promotions? - if @group.feature_available?(:group_webhooks) || show_promotions?
= nav_link(path: 'hooks#index') do = nav_link(path: 'hooks#index') do
= link_to group_hooks_path(@group), title: 'Webhooks' do = link_to group_hooks_path(@group), title: 'Webhooks' do
......
%section.saml_provider
= form_for [group, saml_provider], url: group_saml_providers_path, html: { class: 'form-horizontal' } do |f|
.form-group.row
= form_errors(saml_provider)
= f.label :enabled, _("Enabled"), class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :enabled do
= f.check_box :enabled
= _("Enable SAML authentication for this group")
.form-group.row
= f.label :sso_url, class: 'control-label col-sm-2' do
= _("Identity provider single sign on URL")
.col-sm-10
= f.text_field :sso_url, placeholder: 'e.g. https://example.com/adfs/ls', class: 'form-control'
.help-block
= _('Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called "SSO Service Location", "SAML Token Issuance Endpoint", or "SAML 2.0/W-Federation URL".')
.form-group.row
= f.label :certificate_fingerprint, class: 'control-label col-sm-2' do
= _("Certificate fingerprint")
.col-sm-10
= f.text_field :certificate_fingerprint, placeholder: 'e.g. 0a:1b:2c:3d:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff', class: 'form-control'
.help-block
= _('SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called "Thumbprint".')
.form-actions
= f.submit _("Save changes"), class: 'btn btn-success'
%section.saml-settings.info-well.append-bottom-20
.well-segment
%p= _("To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:")
%ol
%li
= _('Review the process for configuring service providers in your identity provider — in this case, GitLab is the "service provider" or "relying party".')
= link_to help_page_path('administration/auth/saml'), target: '_blank' do
= _("Documentation for popular identity providers")
= icon('external-link')
%li
= _("During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below.")
%li
= (_("Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}") % { icon: icon('external-link'), docsLinkStart: "<a href='#{help_page_path('user/group/saml_sso/index', anchor: 'assertions')}' target='_blank'>", docsLinkEnd: '</a>' }).html_safe
%li
= (_("Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>") % { enable_label: _('Enable SAML authentication for this group'), save_changes: _('Save changes') }).html_safe
%li
= (_("Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider") % { sso_label: _('GitLab single sign on URL') }).html_safe
.well-segment.borderless
= render 'info_row', field: :assertion_consumer_service_url, label_text: _('Assertion consumer service URL')
.help-block= _('Also called "Relying party service URL" or "Reply URL"')
.well-segment.borderless
= render 'info_row', field: :issuer, label_text: 'Identifier'
.help-block= _('Also called "Issuer" or "Relying party trust identifier"')
- if @saml_provider.persisted?
.well-segment.borderless
%label= _("GitLab single sign on URL")
- user_login_url = sso_group_saml_providers_url(@group)
%div= link_to user_login_url, user_login_url
.help-block= _("Used by members to sign in to your group in GitLab")
- value = local_assigns[:value] || @saml_provider.send(field)
= label_tag field, label_text
.clipboard-input-group.input-group
= text_field_tag field, value, class: "js-select-on-focus form-control", readonly: true, aria: { label: label_text }
.input-group-btn
= clipboard_button(target: "##{field}", title: _("Copy URL to clipboard"), class: "btn-default btn-gray")
- page_title _('SAML Single Sign On Settings')
%section.row.prepend-top-default
.col-lg-3.append-bottom-default
%h4.page-title
= _("SAML Single Sign On")
%p
= _("Manage your group’s membership while adding another level of security with SAML.")
= link_to help_page_path('administration/auth/saml'), target: '_blank' do
= _("Learn more")
= icon('external-link')
.col-lg-9
= render 'info'
= render 'form', group: @group, saml_provider: @saml_provider
---
title: Per group SAML settings added behind cookie beta restriction
merge_request: 4549
author:
type: added
class CreateSamlProviders < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :saml_providers do |t|
t.references :group, null: false, index: true
t.boolean :enabled, null: false
t.string :certificate_fingerprint, null: false
t.string :sso_url, null: false
end
add_foreign_key(:saml_providers, :namespaces, column: :group_id, on_delete: :cascade) # rubocop: disable Migration/AddConcurrentForeignKey
end
end
module Gitlab
module Auth
module GroupSaml
class Config < Gitlab::Auth::Saml::Config
def self.options
{}
end
def self.enabled?
Gitlab::Auth::OAuth::Provider.enabled?(:group_saml)
end
end
end
end
end
module OmniAuth
module Strategies
class GroupSaml < SAML
option :name, 'group_saml'
end
end
end
require 'spec_helper'
describe Groups::SamlProvidersController do
let(:saml_provider) { create(:saml_provider, group: group) }
let(:group) { create(:group, :private) }
let(:user) { create(:user) }
before do
request.cookies['enable_group_saml'] = 'true'
sign_in(user)
end
def stub_saml_config(enabled:)
providers = enabled ? %i(group_saml) : []
allow(Devise).to receive(:omniauth_providers).and_return(providers)
end
shared_examples '404 status' do
it 'returns 404 status' do
group.add_owner(user)
subject
expect(response).to have_gitlab_http_status(404)
end
end
shared_examples 'configuration is prevented' do
describe 'GET #show' do
subject { get :show, group_id: group }
it_behaves_like '404 status'
end
describe 'POST #create' do
subject { post :create, group_id: group, saml_provider: { enabled: 'false' } }
it_behaves_like '404 status'
end
describe 'PUT #update' do
subject { put :update, group_id: group, saml_provider: { enabled: 'false' } }
it_behaves_like '404 status'
end
end
context 'when per group saml is unlicensed' do
before do
stub_licensed_features(group_saml: false)
stub_saml_config(enabled: true)
end
it_behaves_like 'configuration is prevented'
end
context 'when per group saml is unconfigured' do
before do
stub_licensed_features(group_saml: true)
stub_saml_config(enabled: false)
end
it_behaves_like 'configuration is prevented'
end
context 'when per group saml feature is enabled' do
before do
stub_saml_config(enabled: true)
stub_licensed_features(group_saml: true)
end
describe 'GET #show' do
subject { get :show, group_id: group }
it 'shows configuration page' do
group.add_owner(user)
subject
expect(response).to render_template 'groups/saml_providers/show'
end
context 'not on a top level group' do
let(:group) { create(:group, :nested) }
it_behaves_like '404 status'
end
context 'with unauthorized user' do
it 'responds with 404' do
group.add_developer(user)
subject
expect(response).to have_http_status(404)
end
end
end
end
end
FactoryBot.define do
factory :saml_provider do
group
certificate_fingerprint '55:44:33:22:11:aa:bb:cc:dd:ee:ff:11:22:33:44:55:66:77:88:99'
sso_url 'https://saml.example.com/adfs/ls'
end
end
require 'spec_helper'
feature 'SAML provider settings' do
include CookieHelper
let(:user) { create(:user) }
let(:group) { create(:group) }
before do
set_beta_cookie
stub_config_setting(url: 'https://localhost')
stub_saml_config
group.add_owner(user)
end
def set_beta_cookie
set_cookie('enable_group_saml', 'true')
end
def submit
click_button('Save changes')
end
def stub_saml_config
stub_saml_authorize_path_helpers
stub_licensed_features(group_saml: true)
allow(Devise).to receive(:omniauth_providers).and_return(%i(group_saml))
end
describe 'settings' do
before do
sign_in(user)
end
it 'displays required information to user' do
visit group_saml_providers_path(group)
within '.saml-settings' do
expect(find_field('Assertion consumer service URL').value).to eq group.build_saml_provider.assertion_consumer_service_url
expect(find_field('Identifier').value).to eq "https://localhost/groups/#{group.full_path}"
end
end
it 'allows creation of new provider' do
visit group_saml_providers_path(group)
fill_in 'Identity provider single sign on URL', with: 'https://localhost:9999/adfs/ls'
fill_in 'Certificate fingerprint', with: 'aa:bb:cc:dd:ee:ff:11:22:33:44:55:66:77:88:99:0a:1b:2c:3d:00'
expect { submit }.to change(SamlProvider, :count).by(1)
end
it 'shows errors if fields missing' do
visit group_saml_providers_path(group)
submit
expect(find('#error_explanation')).to have_text("Certificate fingerprint can't be blank")
end
context 'with existing SAML provider' do
let!(:saml_provider) { create(:saml_provider, group: group) }
it 'allows provider to be disabled' do
visit group_saml_providers_path(group)
find('input#saml_provider_enabled').click
expect { submit }.to change { saml_provider.reload.enabled }.to false
end
it 'displays user login URL' do
visit group_saml_providers_path(group)
login_url = find('label', text: 'GitLab single sign on URL').find('~* a').text
expect(login_url).to end_with "/groups/#{group.full_path}/-/saml/sso"
end
end
end
end
require 'spec_helper'
describe SamlProvider do
describe "Associations" do
it { is_expected.to belong_to :group }
end
describe 'Validations' do
it { is_expected.to validate_presence_of(:group) }
it { is_expected.to validate_presence_of(:sso_url) }
it { is_expected.to validate_presence_of(:certificate_fingerprint) }
it 'expects sso_url to be an https URL' do
expect(subject).to allow_value('https://example.com').for(:sso_url)
expect(subject).not_to allow_value('http://example.com').for(:sso_url)
end
it 'expects certificate_fingerprint to be in an accepted format' do
expect(subject).to allow_value('000030EDC285E01D6B5EA33010A79ADD142F5004').for(:certificate_fingerprint)
expect(subject).to allow_value('00:00:30:ED:C2:85:E0:1D:6B:5E:A3:30:10:A7:9A:DD:14:2F:50:04').for(:certificate_fingerprint)
expect(subject).to allow_value('00-00-30-ED-C2-85-E0-1D-6B-5E-A3-30-10-A7-9A-DD-14-2F-50-04').for(:certificate_fingerprint)
expect(subject).to allow_value('00 00 30 ED C2 85 E0 1D 6B 5E A3 30 10 A7 9A DD 14 2F 50 04').for(:certificate_fingerprint)
sha512 = 'a12bc3d4567ef89ba97f4d1904815d56a497ffc2fe9d5b0f13439a5da73f4f1afde03b1c1b213128e173da24e75cadf224286696f5171540eedf59b684a5f8dd'
expect(subject).to allow_value(sha512).for(:certificate_fingerprint)
too_short = '00:00:30'
invalid_characters = '00@0030EDC285E01D6B5EA33010A79ADD142F5004'
expect(subject).not_to allow_value(too_short).for(:certificate_fingerprint)
expect(subject).not_to allow_value(invalid_characters).for(:certificate_fingerprint)
end
it 'requires group to be top-level' do
group = create(:group)
nested_group = create(:group, :nested)
expect(subject).to allow_value(group).for(:group)
expect(subject).not_to allow_value(nested_group).for(:group)
end
end
describe 'Default values' do
subject(:saml_provider) { described_class.new }
it 'defaults enabled to true' do
expect(subject).to be_enabled
end
end
describe '#settings' do
let(:group) { create(:group, path: 'foo-group')}
let(:settings) { subject.settings }
subject(:saml_provider) { create(:saml_provider, group: group) }
before do
stub_config_setting(url: 'https://localhost')
end
it 'generates callback URL' do
expect(settings[:assertion_consumer_service_url]).to eq "https://localhost/groups/foo-group/-/saml/callback"
end
it 'generates issuer from group' do
expect(settings[:issuer]).to eq "https://localhost/groups/foo-group"
end
it 'includes NameID format' do
expect(settings[:name_identifier_format]).to start_with 'urn:oasis:names:tc:'
end
it 'includes fingerprint' do
expect(settings[:idp_cert_fingerprint]).to eq saml_provider.certificate_fingerprint
end
it 'includes SSO URL' do
expect(settings[:idp_sso_target_url]).to eq saml_provider.sso_url
end
end
end
...@@ -36,6 +36,24 @@ describe GroupPolicy do ...@@ -36,6 +36,24 @@ describe GroupPolicy do
it { is_expected.to be_allowed(:read_epic, :create_epic, :admin_epic, :destroy_epic) } it { is_expected.to be_allowed(:read_epic, :create_epic, :admin_epic, :destroy_epic) }
end end
describe 'per group SAML' do
let(:current_user) { master }
it { is_expected.to be_disallowed(:admin_group_saml) }
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_group_saml) }
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:admin_group_saml) }
end
end
context 'when LDAP sync is not enabled' do context 'when LDAP sync is not enabled' do
context 'owner' do context 'owner' do
let(:current_user) { owner } let(:current_user) { owner }
......
...@@ -8,8 +8,8 @@ msgid "" ...@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-30 17:57+0200\n" "POT-Creation-Date: 2018-04-03 12:44+0100\n"
"PO-Revision-Date: 2018-03-30 17:57+0200\n" "PO-Revision-Date: 2018-04-03 12:44+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -256,6 +256,12 @@ msgstr "" ...@@ -256,6 +256,12 @@ msgstr ""
msgid "Allows you to add and manage Kubernetes clusters." msgid "Allows you to add and manage Kubernetes clusters."
msgstr "" msgstr ""
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
msgstr ""
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect." msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr "" msgstr ""
...@@ -376,6 +382,9 @@ msgstr "" ...@@ -376,6 +382,9 @@ msgstr ""
msgid "Artifacts" msgid "Artifacts"
msgstr "" msgstr ""
msgid "Assertion consumer service URL"
msgstr ""
msgid "Assign custom color like #FF0000" msgid "Assign custom color like #FF0000"
msgstr "" msgstr ""
...@@ -466,6 +475,12 @@ msgstr "" ...@@ -466,6 +475,12 @@ msgstr ""
msgid "Average per day: %{average}" msgid "Average per day: %{average}"
msgstr "" msgstr ""
msgid "Background Color"
msgstr ""
msgid "Background jobs"
msgstr ""
msgid "Begin with the selected commit" msgid "Begin with the selected commit"
msgstr "" msgstr ""
...@@ -711,6 +726,9 @@ msgstr "" ...@@ -711,6 +726,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster" msgid "Cannot modify managed Kubernetes cluster"
msgstr "" msgstr ""
msgid "Certificate fingerprint"
msgstr ""
msgid "Change Weight" msgid "Change Weight"
msgstr "" msgstr ""
...@@ -1276,6 +1294,9 @@ msgstr "" ...@@ -1276,6 +1294,9 @@ msgstr ""
msgid "Compare changes with the last commit" msgid "Compare changes with the last commit"
msgstr "" msgstr ""
msgid "Compare changes with the merge request target branch"
msgstr ""
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same." msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr "" msgstr ""
...@@ -1297,6 +1318,9 @@ msgstr "" ...@@ -1297,6 +1318,9 @@ msgstr ""
msgid "Confidentiality" msgid "Confidentiality"
msgstr "" msgstr ""
msgid "Configure Sidekiq job throttling."
msgstr ""
msgid "Configure the way a user creates a new account." msgid "Configure the way a user creates a new account."
msgstr "" msgstr ""
...@@ -1501,6 +1525,9 @@ msgstr "" ...@@ -1501,6 +1525,9 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}." msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr "" msgstr ""
msgid "Customize colors"
msgstr ""
msgid "Cycle Analytics" msgid "Cycle Analytics"
msgstr "" msgstr ""
...@@ -1584,6 +1611,9 @@ msgstr "" ...@@ -1584,6 +1611,9 @@ msgstr ""
msgid "Dismiss Merge Request promotion" msgid "Dismiss Merge Request promotion"
msgstr "" msgstr ""
msgid "Documentation for popular identity providers"
msgstr ""
msgid "Don't show again" msgid "Don't show again"
msgstr "" msgstr ""
...@@ -1623,6 +1653,9 @@ msgstr "" ...@@ -1623,6 +1653,9 @@ msgstr ""
msgid "Due date" msgid "Due date"
msgstr "" msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
...@@ -1644,6 +1677,9 @@ msgstr "" ...@@ -1644,6 +1677,9 @@ msgstr ""
msgid "Enable Auto DevOps" msgid "Enable Auto DevOps"
msgstr "" msgstr ""
msgid "Enable SAML authentication for this group"
msgstr ""
msgid "Enable and configure InfluxDB metrics." msgid "Enable and configure InfluxDB metrics."
msgstr "" msgstr ""
...@@ -1653,6 +1689,15 @@ msgstr "" ...@@ -1653,6 +1689,15 @@ msgstr ""
msgid "Enable classification control using an external service" msgid "Enable classification control using an external service"
msgstr "" msgstr ""
msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr ""
msgid "Enable the Performance Bar for a given group."
msgstr ""
msgid "Enabled"
msgstr ""
msgid "Environments|An error occurred while fetching the environments." msgid "Environments|An error occurred while fetching the environments."
msgstr "" msgstr ""
...@@ -1839,6 +1884,9 @@ msgstr "" ...@@ -1839,6 +1884,9 @@ msgstr ""
msgid "Files (%{human_size})" msgid "Files (%{human_size})"
msgstr "" msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
msgid "Filter by commit message" msgid "Filter by commit message"
msgstr "" msgstr ""
...@@ -1857,6 +1905,12 @@ msgstr "" ...@@ -1857,6 +1905,12 @@ msgstr ""
msgid "FirstPushedBy|pushed by" msgid "FirstPushedBy|pushed by"
msgstr "" msgstr ""
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
msgid "Fork" msgid "Fork"
msgid_plural "Forks" msgid_plural "Forks"
msgstr[0] "" msgstr[0] ""
...@@ -2069,6 +2123,9 @@ msgstr "" ...@@ -2069,6 +2123,9 @@ msgstr ""
msgid "GitLab Runner section" msgid "GitLab Runner section"
msgstr "" msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
msgid "Gitaly Servers" msgid "Gitaly Servers"
msgstr "" msgstr ""
...@@ -2171,6 +2228,9 @@ msgstr "" ...@@ -2171,6 +2228,9 @@ msgstr ""
msgid "Have your users email" msgid "Have your users email"
msgstr "" msgstr ""
msgid "Header message"
msgstr ""
msgid "Health Check" msgid "Health Check"
msgstr "" msgstr ""
...@@ -2209,6 +2269,9 @@ msgstr "" ...@@ -2209,6 +2269,9 @@ msgstr ""
msgid "Housekeeping successfully started" msgid "Housekeeping successfully started"
msgstr "" msgstr ""
msgid "Identity provider single sign on URL"
msgstr ""
msgid "If enabled, access to projects will be validated on an external service using their classification label." msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr "" msgstr ""
...@@ -2477,6 +2540,9 @@ msgstr "" ...@@ -2477,6 +2540,9 @@ msgstr ""
msgid "Manage project labels" msgid "Manage project labels"
msgstr "" msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
msgid "Mar" msgid "Mar"
msgstr "" msgstr ""
...@@ -2498,6 +2564,9 @@ msgstr "" ...@@ -2498,6 +2564,9 @@ msgstr ""
msgid "Members" msgid "Members"
msgstr "" msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
msgid "Merge Requests" msgid "Merge Requests"
msgstr "" msgstr ""
...@@ -2510,9 +2579,6 @@ msgstr "" ...@@ -2510,9 +2579,6 @@ msgstr ""
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others" msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "" msgstr ""
msgid "MergeRequest|Approved"
msgstr ""
msgid "Merged" msgid "Merged"
msgstr "" msgstr ""
...@@ -2902,6 +2968,9 @@ msgstr "" ...@@ -2902,6 +2968,9 @@ msgstr ""
msgid "Pagination|« First" msgid "Pagination|« First"
msgstr "" msgstr ""
msgid "Part of merge request changes"
msgstr ""
msgid "Password" msgid "Password"
msgstr "" msgstr ""
...@@ -3121,6 +3190,9 @@ msgstr "" ...@@ -3121,6 +3190,9 @@ msgstr ""
msgid "Profiles|your account" msgid "Profiles|your account"
msgstr "" msgstr ""
msgid "Profiling - Performance bar"
msgstr ""
msgid "Programming languages used in this repository" msgid "Programming languages used in this repository"
msgstr "" msgstr ""
...@@ -3447,9 +3519,15 @@ msgstr "" ...@@ -3447,9 +3519,15 @@ msgstr ""
msgid "Revert this merge request" msgid "Revert this merge request"
msgstr "" msgstr ""
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
msgid "Reviewing" msgid "Reviewing"
msgstr "" msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
msgid "Roadmap" msgid "Roadmap"
msgstr "" msgstr ""
...@@ -3462,6 +3540,15 @@ msgstr "" ...@@ -3462,6 +3540,15 @@ msgstr ""
msgid "Running" msgid "Running"
msgstr "" msgstr ""
msgid "SAML Single Sign On"
msgstr ""
msgid "SAML Single Sign On Settings"
msgstr ""
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
msgid "SSH Keys" msgid "SSH Keys"
msgstr "" msgstr ""
...@@ -3573,6 +3660,9 @@ msgstr "" ...@@ -3573,6 +3660,9 @@ msgstr ""
msgid "Set up Koding" msgid "Set up Koding"
msgstr "" msgstr ""
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
msgid "SetPasswordToCloneLink|set a password" msgid "SetPasswordToCloneLink|set a password"
msgstr "" msgstr ""
...@@ -3582,6 +3672,9 @@ msgstr "" ...@@ -3582,6 +3672,9 @@ msgstr ""
msgid "Setup a specific Runner automatically" msgid "Setup a specific Runner automatically"
msgstr "" msgstr ""
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero." msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "" msgstr ""
...@@ -3773,6 +3866,9 @@ msgstr "" ...@@ -3773,6 +3866,9 @@ msgstr ""
msgid "Spam Logs" msgid "Spam Logs"
msgstr "" msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
msgid "Specify the following URL during the Runner setup:" msgid "Specify the following URL during the Runner setup:"
msgstr "" msgstr ""
...@@ -3797,6 +3893,9 @@ msgstr "" ...@@ -3797,6 +3893,9 @@ msgstr ""
msgid "Started" msgid "Started"
msgstr "" msgstr ""
msgid "State your message to activate"
msgstr ""
msgid "Status" msgid "Status"
msgstr "" msgstr ""
...@@ -3818,6 +3917,9 @@ msgstr "" ...@@ -3818,6 +3917,9 @@ msgstr ""
msgid "System Hooks" msgid "System Hooks"
msgstr "" msgstr ""
msgid "System header and footer:"
msgstr ""
msgid "Tag (%{tag_count})" msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})" msgid_plural "Tags (%{tag_count})"
msgstr[0] "" msgstr[0] ""
...@@ -4286,6 +4388,9 @@ msgstr "" ...@@ -4286,6 +4388,9 @@ msgstr ""
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>." msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr "" msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button." msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "" msgstr ""
...@@ -4388,6 +4493,9 @@ msgstr "" ...@@ -4388,6 +4493,9 @@ msgstr ""
msgid "Use your global notification setting" msgid "Use your global notification setting"
msgstr "" msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want." msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr "" msgstr ""
...@@ -4884,6 +4992,9 @@ msgstr "" ...@@ -4884,6 +4992,9 @@ msgstr ""
msgid "mrWidget|Approve" msgid "mrWidget|Approve"
msgstr "" msgstr ""
msgid "mrWidget|Approved"
msgstr ""
msgid "mrWidget|Approved by" msgid "mrWidget|Approved by"
msgstr "" msgstr ""
...@@ -5016,6 +5127,9 @@ msgstr "" ...@@ -5016,6 +5127,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled" msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "" msgstr ""
msgid "mrWidget|Web IDE"
msgstr ""
msgid "mrWidget|You can merge this merge request manually using the" msgid "mrWidget|You can merge this merge request manually using the"
msgstr "" msgstr ""
......
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