doc/user/application_security/img/multi_select_v12_9.png

32 KiB

......@@ -113,6 +113,19 @@ context for a vulnerability as you learn more over time.
![Dismissed vulnerability comment](img/dismissed_info_v12_3.png)
#### Dismissing multiple vulnerabilities
> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
You can dismiss multiple vulnerabilities at once, providing an optional reason.
Selecting the checkboxes on the side of each vulnerability in the list will select that individual vulnerability.
Alternatively, you can select all the vulnerabilities in the list by selecting the checkbox in the table header.
Deselecting the checkbox in the header will deselect all the vulnerabilities in the list.
Once you have selected some vulnerabilities, a menu appears at the top of the table that allows you to select a dismissal reason.
Pressing the "Dismiss Selected" button will dismiss all the selected vulnerabilities at once, with the reason you chose.
![Multiple vulnerability dismissal](img/multi_select_v12_9.png)
### Creating an issue for a vulnerability
You can create an issue for a vulnerability by selecting the **Create issue**
......
......
......@@ -462,6 +462,11 @@ The chart will deploy 5 Elasticsearch nodes: 2 masters, 2 data and 1 client node
with resource requests totalling 0.125 CPU and 4.5GB RAM. Each data node requests 1.5GB of memory,
which makes it incompatible with clusters of `f1-micro` and `g1-small` instance types.
NOTE: **Note:**
The Elastic Stack cluster application is intended as a log aggregation solution and is not related to our
[Advanced Global Search](../search/advanced_global_search.md) functionality, which uses a separate
Elasticsearch cluster.
### Future apps
Interested in contributing a new GitLab managed app? Visit the
......
......
......@@ -69,6 +69,7 @@ The following table depicts the various user permission levels in a project.
| See related issues | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create confidential issue | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ |
| View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
| Assign issues | | ✓ | ✓ | ✓ | ✓ |
| Label issues | | ✓ | ✓ | ✓ | ✓ |
| Set issue weight | | ✓ | ✓ | ✓ | ✓ |
......@@ -83,6 +84,7 @@ The following table depicts the various user permission levels in a project.
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| View project statistics | | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
| Create/edit/delete [Releases](project/releases/index.md)| | | ✓ | ✓ | ✓ |
| Pull from [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
| Publish to [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Upload [Design Management](project/issues/design_management.md) files **(PREMIUM)** | | | ✓ | ✓ | ✓ |
......@@ -152,6 +154,7 @@ The following table depicts the various user permission levels in a project.
1. If **Public pipelines** is enabled in **Project Settings > CI/CD**.
1. Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [Protected Branches](./project/protected_branches.md).
1. If the [branch is protected](./project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings), this depends on the access Developers and Maintainers are given.
1. Guest users can access GitLab [**Releases**](project/releases/index.md) for downloading assets but are not allowed to download the source code nor see repository information like tags and commits.
## Project features permissions
......@@ -198,17 +201,6 @@ Confidential issues can be accessed by reporters and higher permission levels,
as well as by guest users that create a confidential issue. To learn more,
read through the documentation on [permissions and access to confidential issues](project/issues/confidential_issues.md#permissions-and-access-to-confidential-issues).
### Releases permissions
[Project Releases](project/releases/index.md) can be read by project
members with Reporter, Developer, Maintainer, and Owner permissions.
Guest users can access Release pages for downloading assets but
are not allowed to download the source code nor see repository
information such as tags and commits.
Releases can be created, updated, or deleted via [Releases APIs](../api/releases/index.md)
by project Developers, Maintainers, and Owners.
## Group members permissions
NOTE: **Note:**
......
......
......@@ -46,13 +46,15 @@ Logs can be displayed by clicking on a specific pod from [Deploy Boards](../depl
### Logs view
The logs view will contain the last 500 lines for a pod, and has control to filter through:
The logs view lets you filter the logs by:
- Pods.
- [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/issues/5769), environments.
- [From GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21656), [full text search](#full-text-search).
- [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/197879), dates.
Loading more than 500 log lines is possible from [GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/198050) onwards.
Support for pods with multiple containers is coming [in a future release](https://gitlab.com/gitlab-org/gitlab/issues/13404).
Support for historical data is coming [in a future release](https://gitlab.com/gitlab-org/gitlab/issues/196191).
......
......
......@@ -77,17 +77,18 @@ Navigate to the **Design Management** page from any issue by clicking the **Desi
To upload design images, click the **Upload Designs** button and select images to upload.
Designs with the same filename as an existing uploaded design will create a new version
of the design, and will replace the previous version.
Designs cannot be added if the issue has been moved, or its
[discussion is locked](../../discussions/#lock-discussions).
[Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34353) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9,
you can drag and drop designs onto the dedicated dropzone to upload them.
![Drag and drop design uploads](img/design_drag_and_drop_uploads_v12_9.png)
Designs with the same filename as an existing uploaded design will create a new version
of the design, and will replace the previous version. [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34353) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9, dropping a design on an existing uploaded design will also create a new version,
provided the filenames are the same.
Designs cannot be added if the issue has been moved, or its
[discussion is locked](../../discussions/#lock-discussions).
### Skipped designs
Designs with the same filename as an existing uploaded design _and_ whose content has not changed will be skipped.
......
......
......@@ -16,13 +16,6 @@ GitLab's **Releases** are a way to track deliverables in your project. Consider
a snapshot in time of the source, build output, artifacts, and other metadata
associated with a released version of your code.
There are several ways to create a Release:
- In the interface, when you create a new Git tag.
- In the interface, by adding a release note to an existing Git tag.
- Using the [Releases API](../../../api/releases/index.md): we recommend doing this as one of the last
steps in your CI/CD release pipeline.
## Getting started with Releases
Start by giving a [description](#release-description) to the Release and
......@@ -117,7 +110,7 @@ it takes you to the list of Releases.
![Number of Releases](img/releases_count_v12_8.png "Incremental counter of Releases")
For private projects, the number of Releases is displayed to users with Reporter
[permissions](../../permissions.md#releases-permissions) or higher. For public projects,
[permissions](../../permissions.md#project-members-permissions) or higher. For public projects,
it is displayed to every user regardless of their permission level.
### Upcoming Releases
......@@ -130,6 +123,29 @@ Release tag. Once the `released_at` date and time has passed, the badge is autom
![An upcoming release](img/upcoming_release_v12_7.png)
## Creating a Release
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32812) in GitLab
12.9, Releases can be created directly through the GitLab Releases UI.
NOTE: **Note:**
Only users with Developer permissions or higher can create Releases.
Read more about [Release permissions](../../../user/permissions.md#project-members-permissions).
To create a new Release through the GitLab UI:
1. Navigate to **Project overview > Releases** and click the **New release** button.
1. On the **New Tag** page, fill out the tag details.
1. Optionally, in the **Release notes** field, enter the Release's description.
If you leave this field empty, only a tag will be created.
If you populate it, both a tag and a Release will be created.
1. Click **Create tag**.
If you created a release, you can view it at **Project overview > Releases**.
You can also create a Release using the [Releases API](../../../api/releases/index.md#create-a-release):
we recommend doing this as one of the last steps in your CI/CD release pipeline.
## Editing a release
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/26016) in GitLab 12.6.
......
......
doc/user/project/web_ide/img/commit_changes_v12_3.png

192 KiB

doc/user/project/web_ide/img/commit_changes_v12_9.png

665 KiB

......@@ -43,30 +43,33 @@ you can find a more complete list of supported languages in the
NOTE: **Note:**
Single file editing is based on the [Ace Editor](https://ace.c9.io).
## Stage and commit changes
## Commit changes
After making your changes, click the **Commit** button in the bottom left to
review the list of changed files. If you're using GitLab 12.6 or older versions,
click on each file to review the changes and tick the item to stage a file.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4539) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.4 and [brought to GitLab Core](https://gitlab.com/gitlab-org/gitlab-foss/issues/44157) in 10.7.
> - From [GitLab 12.7 onwards](https://gitlab.com/gitlab-org/gitlab/issues/33441),
files were automatically staged.
> - From [GitLab 12.9 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/196609), support for staging files was removed
to prevent loss of unstaged data. All your current changes necessarily have to be
committed or discarded.
From [GitLab 12.7 onward](https://gitlab.com/gitlab-org/gitlab/issues/33441),
all your files will be automatically staged. You still have the option to unstage
changes in case you want to submit them in multiple smaller commits. To unstage
a change, simply click the **Unstage** button when a staged file is open, or click
the undo icon next to **Staged changes** to unstage all changes.
After making your changes, click the **Commit** button on the bottom-left to
review the list of changed files.
Once you have finalized your changes, you can add a commit message, commit the
staged changes and directly create a merge request. In case you don't have write
changes and directly create a merge request. In case you don't have write
access to the selected branch, you will see a warning, but still be able to create
a new branch and start a merge request.
![Commit changes](img/commit_changes_v12_3.png)
To discard a change in a particular file, click the **Discard changes** button on that
file in the changes tab. To discard all the changes, click the trash icon on the
top-right corner of the changes sidebar.
![Commit changes](img/commit_changes_v12_9.png)
## Reviewing changes
Before you commit your changes, you can compare them with the previous commit
by switching to the review mode or selecting the file from the staged files
list.
by switching to the review mode or selecting the file from the list of changes.
An additional review mode is available when you open a merge request, which
shows you a preview of the merge request diff if you commit your changes.
......
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Create missing LfsObjectsProject records for forks
class LinkLfsObjectsProjects
# Model specifically used for migration.
class LfsObjectsProject < ActiveRecord::Base
include EachBatch
self.table_name = 'lfs_objects_projects'
def self.linkable
where(
<<~SQL
lfs_objects_projects.project_id IN (
SELECT fork_network_members.forked_from_project_id
FROM fork_network_members
WHERE fork_network_members.forked_from_project_id IS NOT NULL
)
SQL
)
end
end
# Model specifically used for migration.
class ForkNetworkMember < ActiveRecord::Base
include EachBatch
self.table_name = 'fork_network_members'
def self.without_lfs_object(lfs_object_id)
where(
<<~SQL
fork_network_members.project_id NOT IN (
SELECT lop.project_id
FROM lfs_objects_projects lop
WHERE lop.lfs_object_id = #{lfs_object_id}
)
SQL
)
end
end
BATCH_SIZE = 1000
def perform(start_id, end_id)
lfs_objects_projects =
Gitlab::BackgroundMigration::LinkLfsObjectsProjects::LfsObjectsProject
.linkable
.where(id: start_id..end_id)
return if lfs_objects_projects.empty?
lfs_objects_projects.find_each do |lop|
ForkNetworkMember
.select("#{lop.lfs_object_id}, fork_network_members.project_id, NOW(), NOW()")
.without_lfs_object(lop.lfs_object_id)
.where(forked_from_project_id: lop.project_id)
.each_batch(of: BATCH_SIZE) do |batch, index|
execute <<~SQL
INSERT INTO lfs_objects_projects (lfs_object_id, project_id, created_at, updated_at)
#{batch.to_sql}
SQL
logger.info(message: "LinkLfsObjectsProjects: created missing LfsObjectsProject records for LfsObject #{lop.lfs_object_id}")
end
end
end
private
def execute(sql)
::ActiveRecord::Base.connection.execute(sql)
end
def logger
@logger ||= Gitlab::BackgroundMigration::Logger.build
end
end
end
end
# Template project: https://gitlab.com/pages/jekyll
# Docs: https://docs.gitlab.com/ce/pages/
image: ruby:2.3
image: ruby:2.6
variables:
JEKYLL_ENV: production
LC_ALL: C.UTF-8
before_script:
- gem install bundler
- bundle install
test:
......
......
# Full project: https://gitlab.com/pages/middleman
image: ruby:2.3
image: ruby:2.6
cache:
paths:
......
......
# Full project: https://gitlab.com/pages/nanoc
image: ruby:2.3
image: ruby:2.6
pages:
script:
......
......
# Full project: https://gitlab.com/pages/octopress
image: ruby:2.3
image: ruby:2.6
pages:
script:
......
......
......@@ -68,6 +68,11 @@ msgstr ""
msgid "\"%{path}\" did not exist on \"%{ref}\""
msgstr ""
 
msgid "%d changed file"
msgid_plural "%d changed files"
msgstr[0] ""
msgstr[1] ""
msgid "%d code quality issue"
msgid_plural "%d code quality issues"
msgstr[0] ""
......@@ -186,21 +191,11 @@ msgid_plural "%d shards selected"
msgstr[0] ""
msgstr[1] ""
 
msgid "%d staged change"
msgid_plural "%d staged changes"
msgstr[0] ""
msgstr[1] ""
msgid "%d tag"
msgid_plural "%d tags"
msgstr[0] ""
msgstr[1] ""
 
msgid "%d unstaged change"
msgid_plural "%d unstaged changes"
msgstr[0] ""
msgstr[1] ""
msgid "%d vulnerability dismissed"
msgid_plural "%d vulnerabilities dismissed"
msgstr[0] ""
......@@ -431,9 +426,6 @@ msgstr ""
msgid "%{spanStart}in%{spanEnd} %{errorFn}"
msgstr ""
 
msgid "%{staged} staged and %{unstaged} unstaged changes"
msgstr ""
msgid "%{start} to %{end}"
msgstr ""
 
......@@ -777,9 +769,6 @@ msgstr ""
msgid "<strong>%{group_name}</strong> group members"
msgstr ""
 
msgid "<strong>%{stagedFilesLength} staged</strong> and <strong>%{changedFilesLength} unstaged</strong> changes"
msgstr ""
msgid "<strong>Deletes</strong> source branch"
msgstr ""
 
......@@ -5154,6 +5143,9 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
 
msgid "Configure the Jira integration first on your project's %{strong_start} Settings > Integrations > Jira%{strong_end} page."
msgstr ""
msgid "Configure the way a user creates a new account."
msgstr ""
 
......@@ -6878,13 +6870,10 @@ msgstr ""
msgid "Disabled mirrors can only be enabled by instance owners. It is recommended that you delete them."
msgstr ""
 
msgid "Discard"
msgstr ""
msgid "Discard all changes"
msgstr ""
 
msgid "Discard all unstaged changes?"
msgid "Discard all changes?"
msgstr ""
 
msgid "Discard changes"
......@@ -7492,6 +7481,9 @@ msgstr ""
msgid "Ensure your %{linkStart}environment is part of the deploy stage%{linkEnd} of your CI pipeline to track deployments to your cluster."
msgstr ""
 
msgid "Enter 2FA for Admin Mode"
msgstr ""
msgid "Enter Admin Mode"
msgstr ""
 
......@@ -7531,6 +7523,9 @@ msgstr ""
msgid "Enter one or more user ID separated by commas"
msgstr ""
 
msgid "Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes."
msgstr ""
msgid "Enter the issue description"
msgstr ""
 
......@@ -7645,6 +7640,12 @@ msgstr ""
msgid "Environments|Commit"
msgstr ""
 
msgid "Environments|Currently showing %{fetched} results."
msgstr ""
msgid "Environments|Currently showing all results."
msgstr ""
msgid "Environments|Deploy to..."
msgstr ""
 
......@@ -7678,6 +7679,9 @@ msgstr ""
msgid "Environments|Logs from"
msgstr ""
 
msgid "Environments|Logs from %{start} to %{end}."
msgstr ""
msgid "Environments|New environment"
msgstr ""
 
......@@ -8962,6 +8966,9 @@ msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
 
msgid "From project"
msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
 
......@@ -10608,9 +10615,15 @@ msgstr ""
msgid "Import"
msgstr ""
 
msgid "Import %{status}"
msgstr ""
msgid "Import CSV"
msgstr ""
 
msgid "Import Jira issues"
msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
 
......@@ -10632,6 +10645,9 @@ msgstr ""
msgid "Import issues"
msgstr ""
 
msgid "Import issues from Jira"
msgstr ""
msgid "Import members"
msgstr ""
 
......@@ -11144,6 +11160,9 @@ msgstr ""
msgid "January"
msgstr ""
 
msgid "Jira Issue Import"
msgstr ""
msgid "JiraService|Events for %{noteable_model_name} are disabled."
msgstr ""
 
......@@ -12747,6 +12766,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
 
msgid "Modified"
msgstr ""
msgid "Modified in this version"
msgstr ""
 
......@@ -13028,6 +13050,9 @@ msgstr ""
msgid "New project"
msgstr ""
 
msgid "New release"
msgstr ""
msgid "New runners registration token has been generated!"
msgstr ""
 
......@@ -13621,9 +13646,6 @@ msgstr ""
msgid "Open"
msgstr ""
 
msgid "Open Documentation"
msgstr ""
msgid "Open Selection"
msgstr ""
 
......@@ -16295,6 +16317,9 @@ msgstr ""
msgid "Releases are based on Git tags. We recommend naming tags that fit within semantic versioning, for example %{codeStart}v1.0%{codeEnd}, %{codeStart}v2.0-pre%{codeEnd}."
msgstr ""
 
msgid "Releases documentation"
msgstr ""
msgid "Release|Something went wrong while getting the release details"
msgstr ""
 
......@@ -18680,21 +18705,12 @@ msgstr ""
msgid "Stage & Commit"
msgstr ""
 
msgid "Stage all changes"
msgstr ""
msgid "Stage data updated"
msgstr ""
 
msgid "Stage removed"
msgstr ""
 
msgid "Staged"
msgstr ""
msgid "Staged %{type}"
msgstr ""
msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
msgstr ""
 
......@@ -19304,7 +19320,7 @@ msgstr ""
msgid "TagsPage|Optionally, add a message to the tag. Leaving this blank creates a %{link_start}lightweight tag.%{link_end}"
msgstr ""
 
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
msgid "TagsPage|Optionally, create a public Release of your project, based on this tag. Release notes are displayed on the %{releases_page_link_start}Releases%{link_end} page. %{docs_link_start}More information%{link_end}"
msgstr ""
 
msgid "TagsPage|Release notes"
......@@ -19851,6 +19867,9 @@ msgstr ""
msgid "There are no archived projects yet"
msgstr ""
 
msgid "There are no changes"
msgstr ""
msgid "There are no charts configured for this page"
msgstr ""
 
......@@ -19887,12 +19906,6 @@ msgstr ""
msgid "There are no projects shared with this group yet"
msgstr ""
 
msgid "There are no staged changes"
msgstr ""
msgid "There are no unstaged changes"
msgstr ""
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
 
......@@ -21094,6 +21107,9 @@ msgstr ""
msgid "Two-Factor Authentication"
msgstr ""
 
msgid "Two-Factor Authentication code"
msgstr ""
msgid "Two-factor Authentication"
msgstr ""
 
......@@ -21304,18 +21320,6 @@ msgstr ""
msgid "Unschedule job"
msgstr ""
 
msgid "Unstage"
msgstr ""
msgid "Unstage all changes"
msgstr ""
msgid "Unstaged"
msgstr ""
msgid "Unstaged %{type}"
msgstr ""
msgid "Unstar"
msgstr ""
 
......@@ -22799,6 +22803,9 @@ msgstr ""
msgid "You can only transfer the project to namespaces you manage."
msgstr ""
 
msgid "You can only upload one design when dropping onto an existing design."
msgstr ""
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
 
......@@ -22952,6 +22959,9 @@ msgstr ""
msgid "You must set up incoming email before it becomes active."
msgstr ""
 
msgid "You must upload a file with the same file name when dropping onto an existing design."
msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
 
......@@ -22988,7 +22998,7 @@ msgstr ""
msgid "You will lose all changes you've made to this file. This action cannot be undone."
msgstr ""
 
msgid "You will lose all the unstaged changes you've made in this project. This action cannot be undone."
msgid "You will lose all uncommitted changes you've made in this project. This action cannot be undone."
msgstr ""
 
msgid "You will need to update your local repositories to point to the new location."
......
......
......@@ -68,7 +68,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
# triggering the auth form will request admin mode
get :new
post :create, params: { password: user.password }
post :create, params: { user: { password: user.password } }
expect(response).to redirect_to admin_root_path
expect(controller.current_user_mode.admin_mode?).to be(true)
......@@ -82,7 +82,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
# triggering the auth form will request admin mode
get :new
post :create, params: { password: '' }
post :create, params: { user: { password: '' } }
expect(response).to render_template :new
expect(controller.current_user_mode.admin_mode?).to be(false)
......@@ -95,7 +95,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
# do not trigger the auth form
post :create, params: { password: user.password }
post :create, params: { user: { password: user.password } }
expect(response).to redirect_to(new_admin_session_path)
expect(controller.current_user_mode.admin_mode?).to be(false)
......@@ -110,12 +110,118 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :new
Timecop.freeze(Gitlab::Auth::CurrentUserMode::ADMIN_MODE_REQUESTED_GRACE_PERIOD.from_now) do
post :create, params: { password: user.password }
post :create, params: { user: { password: user.password } }
expect(response).to redirect_to(new_admin_session_path)
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
context 'when using two-factor authentication via OTP' do
let(:user) { create(:admin, :two_factor) }
def authenticate_2fa(user_params)
post(:create, params: { user: user_params }, session: { otp_user_id: user.id })
end
it 'requests two factor after a valid password is provided' do
expect(controller.current_user_mode.admin_mode?).to be(false)
# triggering the auth form will request admin mode
get :new
post :create, params: { user: { password: user.password } }
expect(response).to render_template('admin/sessions/two_factor')
expect(controller.current_user_mode.admin_mode?).to be(false)
end
it 'can login with valid otp' do
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
controller.current_user_mode.request_admin_mode!
authenticate_2fa(otp_attempt: user.current_otp)
expect(response).to redirect_to admin_root_path
expect(controller.current_user_mode.admin_mode?).to be(true)
end
it 'cannot login with invalid otp' do
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.current_user_mode.request_admin_mode!
authenticate_2fa(otp_attempt: 'invalid')
expect(response).to render_template('admin/sessions/two_factor')
expect(controller.current_user_mode.admin_mode?).to be(false)
end
context 'with password authentication disabled' do
before do
stub_application_setting(password_authentication_enabled_for_web: false)
end
it 'allows 2FA stage of non-password login' do
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
controller.current_user_mode.request_admin_mode!
authenticate_2fa(otp_attempt: user.current_otp)
expect(response).to redirect_to admin_root_path
expect(controller.current_user_mode.admin_mode?).to be(true)
end
end
end
context 'when using two-factor authentication via U2F' do
let(:user) { create(:admin, :two_factor_via_u2f) }
def authenticate_2fa_u2f(user_params)
post(:create, params: { user: user_params }, session: { otp_user_id: user.id })
end
it 'requests two factor after a valid password is provided' do
expect(controller.current_user_mode.admin_mode?).to be(false)
# triggering the auth form will request admin mode
get :new
post :create, params: { user: { password: user.password } }
expect(response).to render_template('admin/sessions/two_factor')
expect(controller.current_user_mode.admin_mode?).to be(false)
end
it 'can login with valid auth' do
allow(U2fRegistration).to receive(:authenticate).and_return(true)
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
controller.current_user_mode.request_admin_mode!
authenticate_2fa_u2f(login: user.username, device_response: '{}')
expect(response).to redirect_to admin_root_path
expect(controller.current_user_mode.admin_mode?).to be(true)
end
it 'cannot login with invalid auth' do
allow(U2fRegistration).to receive(:authenticate).and_return(false)
expect(controller.current_user_mode.admin_mode?).to be(false)
controller.current_user_mode.request_admin_mode!
authenticate_2fa_u2f(login: user.username, device_response: '{}')
expect(response).to render_template('admin/sessions/two_factor')
expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
end
end
......@@ -136,7 +242,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
expect(controller.current_user_mode.admin_mode?).to be(false)
get :new
post :create, params: { password: user.password }
post :create, params: { user: { password: user.password } }
expect(controller.current_user_mode.admin_mode?).to be(true)
post :destroy
......
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::Import::JiraController do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
context 'with anonymous user' do
before do
stub_feature_flags(jira_issue_import: true)
end
context 'get show' do
it 'redirects to issues page' do
get :show, params: { namespace_id: project.namespace, project_id: project }
expect(response).to redirect_to(new_user_session_path)
end
end
context 'post import' do
it 'redirects to issues page' do
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'Test' }
expect(response).to redirect_to(new_user_session_path)
end
end
end
context 'with logged in user' do
before do
sign_in(user)
project.add_maintainer(user)
end
context 'when feature flag not enabled' do
before do
stub_feature_flags(jira_issue_import: false)
end
context 'get show' do
it 'redirects to issues page' do
get :show, params: { namespace_id: project.namespace, project_id: project }
expect(response).to redirect_to(project_issues_path(project))
end
end
context 'post import' do
it 'redirects to issues page' do
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'Test' }
expect(response).to redirect_to(project_issues_path(project))
end
end
end
context 'when feature flag enabled' do
before do
stub_feature_flags(jira_issue_import: true)
end
context 'when jira service is enabled for the project' do
let_it_be(:jira_service) { create(:jira_service, project: project) }
context 'when running jira import first time' do
context 'get show' do
it 'renders show template' do
allow(JIRA::Resource::Project).to receive(:all).and_return([])
expect(project.import_state).to be_nil
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
expect(response).to render_template :show
end
end
context 'post import' do
it 'creates import state' do
expect(project.import_state).to be_nil
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'Test' }
project.reload
jira_project = project.import_data.data.dig('jira', 'projects').first
expect(project.import_type).to eq 'jira'
expect(project.import_state.status).to eq 'scheduled'
expect(jira_project['key']).to eq 'Test'
expect(response).to redirect_to(project_import_jira_path(project))
end
end
end
context 'when import state is scheduled' do
let_it_be(:import_state) { create(:import_state, project: project, status: :scheduled) }
context 'get show' do
it 'renders import status' do
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
expect(project.import_state.status).to eq 'scheduled'
expect(flash.now[:notice]).to eq 'Import scheduled'
end
end
context 'post import' do
before do
project.reload
project.create_import_data(
data: {
'jira': {
'projects': [{ 'key': 'Test', scheduled_at: 5.days.ago, scheduled_by: { user_id: user.id, name: user.name } }]
}
}
)
end
it 'uses the existing import data' do
expect(controller).not_to receive(:schedule_import)
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
expect(response).to redirect_to(project_import_jira_path(project))
end
end
end
context 'when jira import ran before' do
let_it_be(:import_state) { create(:import_state, project: project, status: :finished) }
context 'get show' do
it 'renders import status' do
allow(JIRA::Resource::Project).to receive(:all).and_return([])
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
expect(project.import_state.status).to eq 'finished'
expect(flash.now[:notice]).to eq 'Import finished'
end
end
context 'post import' do
before do
project.reload
project.create_import_data(
data: {
'jira': {
'projects': [{ 'key': 'Test', scheduled_at: 5.days.ago, scheduled_by: { user_id: user.id, name: user.name } }]
}
}
)
end
it 'uses the existing import data' do
expect(controller).to receive(:schedule_import).and_call_original
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
project.reload
expect(project.import_state.status).to eq 'scheduled'
jira_imported_projects = project.import_data.data.dig('jira', 'projects')
expect(jira_imported_projects.size).to eq 2
expect(jira_imported_projects.first['key']).to eq 'Test'
expect(jira_imported_projects.last['key']).to eq 'New Project'
expect(response).to redirect_to(project_import_jira_path(project))
end
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
include TermsHelper
include UserLoginHelper
describe 'with two-factor authentication', :js do
def enter_code(code)
fill_in 'user_otp_attempt', with: code
click_button 'Verify code'
end
context 'with valid username/password' do
let(:user) { create(:admin, :two_factor) }
context 'using one-time code' do
it 'blocks login if we reuse the same code immediately' do
gitlab_sign_in(user, remember: true)
expect(page).to have_content('Two-Factor Authentication')
repeated_otp = user.current_otp
enter_code(repeated_otp)
gitlab_enable_admin_mode_sign_in(user)
expect(page).to have_content('Two-Factor Authentication')
enter_code(repeated_otp)
expect(current_path).to eq admin_session_path
expect(page).to have_content('Invalid two-factor code')
end
context 'not re-using codes' do
before do
gitlab_sign_in(user, remember: true)
expect(page).to have_content('Two-Factor Authentication')
enter_code(user.current_otp)
gitlab_enable_admin_mode_sign_in(user)
expect(page).to have_content('Two-Factor Authentication')
end
it 'allows login with valid code' do
# Cannot reuse the TOTP
Timecop.travel(30.seconds.from_now) do
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
expect(page).to have_content('Admin mode enabled')
end
end
it 'blocks login with invalid code' do
# Cannot reuse the TOTP
Timecop.travel(30.seconds.from_now) do
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
end
end
it 'allows login with invalid code, then valid code' do
# Cannot reuse the TOTP
Timecop.travel(30.seconds.from_now) do
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
expect(page).to have_content('Admin mode enabled')
end
end
context 'using backup code' do
let(:codes) { user.generate_otp_backup_codes! }
before do
expect(codes.size).to eq 10
# Ensure the generated codes get saved
user.save
end
context 'with valid code' do
it 'allows login' do
enter_code(codes.sample)
expect(current_path).to eq admin_root_path
expect(page).to have_content('Admin mode enabled')
end
it 'invalidates the used code' do
expect { enter_code(codes.sample) }
.to change { user.reload.otp_backup_codes.size }.by(-1)
end
end
context 'with invalid code' do
it 'blocks login' do
code = codes.sample
expect(user.invalidate_otp_backup_code!(code)).to eq true
user.save!
expect(user.reload.otp_backup_codes.size).to eq 9
enter_code(code)
expect(page).to have_content('Invalid two-factor code.')
end
end
end
end
end
context 'when logging in via omniauth' do
let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml')}
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
end
before do
stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
providers: [mock_saml_config_with_upstream_two_factor_authn_contexts])
end
context 'when authn_context is worth two factors' do
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
.gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
end
it 'signs user in without prompting for second factor' do
sign_in_using_saml!
expect(page).not_to have_content('Two-Factor Authentication')
enable_admin_mode_using_saml!
expect(page).not_to have_content('Two-Factor Authentication')
expect(current_path).to eq admin_root_path
expect(page).to have_content('Admin mode enabled')
end
end
context 'when two factor authentication is required' do
it 'shows 2FA prompt after omniauth login' do
sign_in_using_saml!
expect(page).to have_content('Two-Factor Authentication')
enter_code(user.current_otp)
enable_admin_mode_using_saml!
expect(page).to have_content('Two-Factor Authentication')
# Cannot reuse the TOTP
Timecop.travel(30.seconds.from_now) do
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
expect(page).to have_content('Admin mode enabled')
end
end
end
def sign_in_using_saml!
gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response)
end
def enable_admin_mode_using_saml!
gitlab_enable_admin_mode_sign_in_via('saml', user, 'my-uid', mock_saml_response)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Admin Mode Logout', :js, :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
include TermsHelper
include UserLoginHelper
let(:user) { create(:admin) }
before do
gitlab_sign_in(user)
gitlab_enable_admin_mode_sign_in(user)
visit admin_root_path
end
it 'disable removes admin mode and redirects to root page' do
gitlab_disable_admin_mode
expect(current_path).to eq root_path
expect(page).to have_link(href: new_admin_session_path)
end
it 'disable shows flash notice' do
gitlab_disable_admin_mode
expect(page).to have_selector('.flash-notice')
end
context 'on a read-only instance' do
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
it 'disable removes admin mode and redirects to root page' do
gitlab_disable_admin_mode
expect(current_path).to eq root_path
expect(page).to have_link(href: new_admin_session_path)
end
end
end