Commit ec3150db authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 221b1159d254
......@@ -8,6 +8,11 @@ export default {
required: true,
default: __('Search'),
},
focused: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return { searchQuery: this.value };
......@@ -16,6 +21,11 @@ export default {
searchQuery(query) {
this.$emit('input', query);
},
focused(val) {
if (val) {
this.$refs.searchInput.focus();
}
},
},
};
</script>
......@@ -23,6 +33,7 @@ export default {
<template>
<div class="dropdown-input">
<input
ref="searchInput"
v-model="searchQuery"
:placeholder="placeholderText"
class="dropdown-input-field"
......
<script>
import { GlPagination } from '@gitlab/ui';
import {
PAGINATION_UI_BUTTON_LIMIT,
UI_LIMIT,
SPREAD,
PREV,
NEXT,
FIRST,
LAST,
LABEL_FIRST_PAGE,
LABEL_PREV_PAGE,
LABEL_NEXT_PAGE,
LABEL_LAST_PAGE,
} from '~/vue_shared/components/pagination/constants';
export default {
components: {
GlPagination,
},
props: {
/**
This function will take the information given by the pagination component
......@@ -46,113 +49,34 @@ export default {
},
},
computed: {
prev() {
return this.pageInfo.previousPage;
},
next() {
return this.pageInfo.nextPage;
},
getItems() {
const { totalPages, nextPage, previousPage, page } = this.pageInfo;
const items = [];
if (page > 1) {
items.push({ title: FIRST, first: true });
}
if (previousPage) {
items.push({ title: PREV, prev: true });
} else {
items.push({ title: PREV, disabled: true, prev: true });
}
if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
if (totalPages) {
const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, totalPages);
for (let i = start; i <= end; i += 1) {
const isActive = i === page;
items.push({ title: i, active: isActive, page: true });
}
if (totalPages - page > PAGINATION_UI_BUTTON_LIMIT) {
items.push({ title: SPREAD, separator: true, page: true });
}
}
if (nextPage) {
items.push({ title: NEXT, next: true });
} else {
items.push({ title: NEXT, disabled: true, next: true });
}
if (totalPages && totalPages - page >= 1) {
items.push({ title: LAST, last: true });
}
return items;
},
showPagination() {
return this.pageInfo.nextPage || this.pageInfo.previousPage;
},
},
methods: {
changePage(text, isDisabled) {
if (isDisabled) return;
const { totalPages, nextPage, previousPage } = this.pageInfo;
switch (text) {
case SPREAD:
break;
case LAST:
this.change(totalPages);
break;
case NEXT:
this.change(nextPage);
break;
case PREV:
this.change(previousPage);
break;
case FIRST:
this.change(1);
break;
default:
this.change(Number(text));
break;
}
},
hideOnSmallScreen(item) {
return !item.first && !item.last && !item.next && !item.prev && !item.active;
},
},
prevText: PREV,
nextText: NEXT,
labelFirstPage: LABEL_FIRST_PAGE,
labelPrevPage: LABEL_PREV_PAGE,
labelNextPage: LABEL_NEXT_PAGE,
labelLastPage: LABEL_LAST_PAGE,
};
</script>
<template>
<div v-if="showPagination" class="gl-pagination prepend-top-default">
<ul class="pagination justify-content-center">
<li
v-for="(item, index) in getItems"
:key="index"
:class="{
page: item.page,
'js-previous-button': item.prev,
'js-next-button': item.next,
'js-last-button': item.last,
'js-first-button': item.first,
'd-none d-md-block': hideOnSmallScreen(item),
separator: item.separator,
active: item.active,
disabled: item.disabled || item.separator,
}"
class="page-item"
>
<button type="button" class="page-link" @click="changePage(item.title, item.disabled)">
{{ item.title }}
</button>
</li>
</ul>
</div>
<gl-pagination
v-if="showPagination"
class="justify-content-center prepend-top-default"
v-bind="$attrs"
:value="pageInfo.page"
:per-page="pageInfo.perPage"
:total-items="pageInfo.total"
:prev-page="pageInfo.previousPage"
:prev-text="$options.prevText"
:next-page="pageInfo.nextPage"
:next-text="$options.nextText"
:label-first-page="$options.labelFirstPage"
:label-prev-page="$options.labelPrevPage"
:label-next-page="$options.labelNextPage"
:label-last-page="$options.labelLastPage"
@input="change"
/>
</template>
......@@ -6,17 +6,23 @@ class Admin::SessionsController < ApplicationController
before_action :user_is_admin!
def new
# Renders a form in which the admin can enter their password
if current_user_mode.admin_mode?
redirect_to redirect_path, notice: _('Admin mode already enabled')
else
current_user_mode.request_admin_mode! unless current_user_mode.admin_mode_requested?
store_location_for(:redirect, redirect_path)
end
end
def create
if current_user_mode.enable_admin_mode!(password: params[:password])
redirect_location = stored_location_for(:redirect) || admin_root_path
redirect_to safe_redirect_path(redirect_location)
redirect_to redirect_path, notice: _('Admin mode enabled')
else
flash.now[:alert] = _('Invalid Login or password')
flash.now[:alert] = _('Invalid login or password')
render :new
end
rescue Gitlab::Auth::CurrentUserMode::NotRequestedError
redirect_to new_admin_session_path, alert: _('Re-authentication period expired or never requested. Please try again')
end
def destroy
......@@ -30,4 +36,19 @@ def destroy
def user_is_admin!
render_404 unless current_user&.admin?
end
def redirect_path
redirect_to_path = safe_redirect_path(stored_location_for(:redirect)) || safe_redirect_path_for_url(request.referer)
if redirect_to_path &&
excluded_redirect_paths.none? { |excluded| redirect_to_path.include?(excluded) }
redirect_to_path
else
admin_root_path
end
end
def excluded_redirect_paths
[new_admin_session_path, admin_session_path]
end
end
......@@ -16,6 +16,7 @@ class ApplicationController < ActionController::Base
include ConfirmEmailWarning
include Gitlab::Tracking::ControllerConcern
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
before_action :authenticate_user!, except: [:route_not_found]
before_action :enforce_terms!, if: :should_enforce_terms?
......@@ -41,7 +42,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
helper_method :can?
helper_method :current_user_mode
helper_method :import_sources_enabled?, :github_import_enabled?,
:gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?,
......@@ -546,10 +546,6 @@ def allow_gitaly_ref_name_caching
end
end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
# A user requires a role and have the setup_for_company attribute set when they are part of the experimental signup
# flow (executed by the Growth team). Users are redirected to the welcome page when their role is required and the
# experiment is enabled for the current user.
......
......@@ -18,6 +18,7 @@ def authenticate_admin!
return unless Feature.enabled?(:user_mode_in_session)
unless current_user_mode.admin_mode?
current_user_mode.request_admin_mode!
store_location_for(:redirect, request.fullpath) if storable_location?
redirect_to(new_admin_session_path, notice: _('Re-authentication required'))
end
......
# frozen_string_literal: true
module InitializesCurrentUserMode
extend ActiveSupport::Concern
included do
helper_method :current_user_mode
end
def current_user_mode
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end
end
......@@ -33,6 +33,8 @@ def sessionless_sign_in(user)
end
def enable_admin_mode!
current_user_mode.enable_admin_mode!(skip_password_validation: true) if Feature.enabled?(:user_mode_in_session)
return unless Feature.enabled?(:user_mode_in_session)
current_user_mode.enable_sessionless_admin_mode!
end
end
......@@ -6,6 +6,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include PageLayoutHelper
include OauthApplications
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
before_action :verify_user_oauth_applications_enabled, except: :index
before_action :authenticate_user!
......
......@@ -2,6 +2,8 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
include Gitlab::Experimentation::ControllerConcern
include InitializesCurrentUserMode
layout 'profile'
# Overridden from Doorkeeper::AuthorizationsController to
......
......@@ -4,6 +4,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
include AuthHelper
include InitializesCurrentUserMode
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
......@@ -94,8 +95,12 @@ def omniauth_flow(auth_module, identity_linker: nil)
return render_403 unless link_provider_allowed?(oauth['provider'])
log_audit_event(current_user, with: oauth['provider'])
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
if Feature.enabled?(:user_mode_in_session)
return admin_mode_flow if current_user_mode.admin_mode_requested?
end
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth, session)
link_identity(identity_linker)
if identity_linker.changed?
......@@ -239,6 +244,24 @@ def store_redirect_fragment(redirect_fragment)
store_location_for(:user, uri.to_s)
end
end
def admin_mode_flow
if omniauth_identity_matches_current_user?
current_user_mode.enable_admin_mode!(skip_password_validation: true)
redirect_to stored_location_for(:redirect) || admin_root_path, notice: _('Admin mode enabled')
else
fail_admin_mode_invalid_credentials
end
end
def omniauth_identity_matches_current_user?
current_user.matches_identity?(oauth['provider'], oauth['uid'])
end
def fail_admin_mode_invalid_credentials
redirect_to new_admin_session_path, alert: _('Invalid login or password')
end
end
OmniauthCallbacksController.prepend_if_ee('EE::OmniauthCallbacksController')
......@@ -159,3 +159,5 @@ class ProjectType < BaseObject
resolver: Resolvers::Projects::SnippetsResolver
end
end
Types::ProjectType.prepend_if_ee('::EE::Types::ProjectType')
# frozen_string_literal: true
module ContainerExpirationPoliciesHelper
def cadence_options
ContainerExpirationPolicy.cadence_options.map do |key, val|
{ key: key.to_s, label: val }
end
end
def keep_n_options
ContainerExpirationPolicy.keep_n_options.map do |key, val|
{ key: key, label: val }
end
end
def older_than_options
ContainerExpirationPolicy.older_than_options.map do |key, val|
{ key: key.to_s, label: val }
end
end
end
......@@ -87,7 +87,7 @@ def get_header_links
end
if Feature.enabled?(:user_mode_in_session)
if current_user&.admin? && current_user_mode&.admin_mode?
if current_user_mode.admin_mode?
links << :admin_mode
end
end
......
# frozen_string_literal: true
class ContainerExpirationPolicy < ApplicationRecord
belongs_to :project, inverse_of: :container_expiration_policy
validates :project, presence: true
validates :enabled, inclusion: { in: [true, false] }
validates :cadence, presence: true, inclusion: { in: ->(_) { self.cadence_options.stringify_keys } }
validates :older_than, inclusion: { in: ->(_) { self.older_than_options.stringify_keys } }, allow_nil: true
validates :keep_n, inclusion: { in: ->(_) { self.keep_n_options.keys } }, allow_nil: true
def self.keep_n_options
{
1 => _('%{tags} tag per image name') % { tags: 1 },
5 => _('%{tags} tags per image name') % { tags: 5 },
10 => _('%{tags} tags per image name') % { tags: 10 },
25 => _('%{tags} tags per image name') % { tags: 25 },
50 => _('%{tags} tags per image name') % { tags: 50 },
100 => _('%{tags} tags per image name') % { tags: 100 }
}
end
def self.cadence_options
{
'1d': _('Every day'),
'7d': _('Every week'),
'14d': _('Every two weeks'),
'1month': _('Every month'),
'3month': _('Every three months')
}
end
def self.older_than_options
{
'7d': _('%{days} days until tags are automatically removed') % { days: 7 },
'14d': _('%{days} days until tags are automatically removed') % { days: 14 },
'30d': _('%{days} days until tags are automatically removed') % { days: 30 },
'90d': _('%{days} days until tags are automatically removed') % { days: 90 }
}
end
end
......@@ -123,8 +123,10 @@ def clean_path(path)
def find_by_pages_host(host)
gitlab_host = "." + Settings.pages.host.downcase
name = host.downcase.delete_suffix(gitlab_host)
host = host.downcase
return unless host.ends_with?(gitlab_host)
name = host.delete_suffix(gitlab_host)
Namespace.find_by_full_path(name)
end
end
......
......@@ -97,8 +97,11 @@ class Project < ApplicationRecord
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
after_create :create_container_expiration_policy,
unless: :container_expiration_policy
after_create :create_pages_metadatum,
unless: :pages_metadatum
unless: :pages_metadatum
after_create :set_timestamps_for_create
after_update :update_forks_visibility_level
......@@ -248,6 +251,7 @@ class Project < ApplicationRecord
# which is not managed by the DB. Hence we're still using dependent: :destroy
# here.
has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :container_expiration_policy, inverse_of: :project
has_many :commit_statuses
# The relation :all_pipelines is intended to be used when we want to get the
......@@ -305,6 +309,7 @@ class Project < ApplicationRecord
accepts_nested_attributes_for :import_data
accepts_nested_attributes_for :auto_devops, update_only: true
accepts_nested_attributes_for :ci_cd_settings, update_only: true
accepts_nested_attributes_for :container_expiration_policy, update_only: true
accepts_nested_attributes_for :remote_mirrors,
allow_destroy: true,
......
......@@ -996,6 +996,10 @@ def ldap_identity
@ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def matches_identity?(provider, extern_uid)
identities.where(provider: provider, extern_uid: extern_uid).exists?
end
def project_deploy_keys
DeployKey.in_projects(authorized_projects.select(:id)).distinct(:id)
end
......
- if any_form_based_providers_enabled?
- if password_authentication_enabled_for_web?
.login-box.tab-pane{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
- elsif password_authentication_enabled_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
......@@ -7,9 +7,16 @@
#signin-container
= render 'admin/sessions/tabs_normal'
.tab-content
- if password_authentication_enabled_for_web?
= render 'admin/sessions/signin_box'
- else
-# Show a message if none of the mechanisms above are enabled
- if !current_user.require_password_creation_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'admin/sessions/new_base'
- if omniauth_enabled? && button_based_providers_enabled?
.clearfix
= render 'devise/shared/omniauth_box'
-# Show a message if none of the mechanisms above are enabled
- if current_user.require_password_creation_for_web? && !omniauth_enabled?
.prepend-top-default.center
= _('No authentication methods configured.')
---
title: Create container expiration policies for projects
merge_request: 20412
author:
type: added
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