Commit a59c1230 authored by Clement Ho's avatar Clement Ho

Merge branch 'master' into 'webpack-mirrors'

# Conflicts:
#   config/webpack.config.js
parents ec3a5335 824eeb4a
import U2FRegister from './u2f/register';
import U2FRegister from '~/u2f/register';
document.addEventListener('DOMContentLoaded', () => {
const twoFactorNode = document.querySelector('.js-two-factor-auth');
......
......@@ -62,19 +62,27 @@ module UploadsActions
end
def build_uploader_from_upload
return nil unless params[:secret] && params[:filename]
return unless uploader = build_uploader
upload_path = uploader_class.upload_path(params[:secret], params[:filename])
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_path)
upload_paths = uploader.upload_paths(params[:filename])
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_paths)
upload&.build_uploader
end
def build_uploader_from_params
return unless uploader = build_uploader
uploader.retrieve_from_store!(params[:filename])
uploader
end
def build_uploader
return unless params[:secret] && params[:filename]
uploader = uploader_class.new(model, secret: params[:secret])
return nil unless uploader.model_valid?
return unless uploader.model_valid?
uploader.retrieve_from_store!(params[:filename])
uploader
end
......
......@@ -32,8 +32,11 @@ class FileUploader < GitlabUploader
)
end
def self.base_dir(model)
model_path_segment(model)
def self.base_dir(model, store = Store::LOCAL)
decorated_model = model
decorated_model = Storage::HashedProject.new(model) if store == Store::REMOTE
model_path_segment(decorated_model)
end
# used in migrations and import/exports
......@@ -51,21 +54,24 @@ class FileUploader < GitlabUploader
#
# Returns a String without a trailing slash
def self.model_path_segment(model)
if model.hashed_storage?(:attachments)
model.disk_path
case model
when Storage::HashedProject then model.disk_path
else
model.full_path
model.hashed_storage?(:attachments) ? model.disk_path : model.full_path
end
end
def self.upload_path(secret, identifier)
File.join(secret, identifier)
end
def self.generate_secret
SecureRandom.hex
end
def upload_paths(filename)
[
File.join(secret, filename),
File.join(base_dir(Store::REMOTE), secret, filename)
]
end
attr_accessor :model
def initialize(model, mounted_as = nil, **uploader_context)
......@@ -75,8 +81,10 @@ class FileUploader < GitlabUploader
apply_context!(uploader_context)
end
def base_dir
self.class.base_dir(@model)
# enforce the usage of Hashed storage when storing to
# remote store as the FileMover doesn't support OS
def base_dir(store = nil)
self.class.base_dir(@model, store || object_store)
end
# we don't need to know the actual path, an uploader instance should be
......@@ -86,15 +94,19 @@ class FileUploader < GitlabUploader
end
def upload_path
self.class.upload_path(dynamic_segment, identifier)
end
def model_path_segment
self.class.model_path_segment(@model)
if file_storage?
# Legacy path relative to project.full_path
File.join(dynamic_segment, identifier)
else
File.join(store_dir, identifier)
end
end
def store_dir
File.join(base_dir, dynamic_segment)
def store_dirs
{
Store::LOCAL => File.join(base_dir, dynamic_segment),
Store::REMOTE => File.join(base_dir(ObjectStorage::Store::REMOTE), dynamic_segment)
}
end
def markdown_link
......
......@@ -4,7 +4,7 @@ class NamespaceFileUploader < FileUploader
options.storage_path
end
def self.base_dir(model)
def self.base_dir(model, _store = nil)
File.join(options.base_dir, 'namespace', model_path_segment(model))
end
......@@ -20,7 +20,7 @@ class NamespaceFileUploader < FileUploader
def store_dirs
{
Store::LOCAL => File.join(base_dir, dynamic_segment),
Store::REMOTE => File.join('namespace', model_path_segment, dynamic_segment)
Store::REMOTE => File.join('namespace', self.class.model_path_segment(model), dynamic_segment)
}
end
end
......@@ -4,7 +4,7 @@ class PersonalFileUploader < FileUploader
options.storage_path
end
def self.base_dir(model)
def self.base_dir(model, _store = nil)
File.join(options.base_dir, model_path_segment(model))
end
......@@ -34,7 +34,7 @@ class PersonalFileUploader < FileUploader
def store_dirs
{
Store::LOCAL => File.join(base_dir, dynamic_segment),
Store::REMOTE => File.join(model_path_segment, dynamic_segment)
Store::REMOTE => File.join(self.class.model_path_segment(model), dynamic_segment)
}
end
......
......@@ -2,10 +2,6 @@
- add_to_breadcrumbs("Two-Factor Authentication", profile_account_path)
- @content_class = "limit-container-width" unless fluid_layout
- content_for :page_specific_javascripts do
= webpack_bundle_tag('two_factor_auth')
.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
.row.prepend-top-default
.col-lg-4
......
---
title: expose more metrics in merge requests api
merge_request: 16589
author: haseebeqx
type: added
......@@ -51,7 +51,6 @@ function generateEntries() {
monitoring: './monitoring/monitoring_bundle.js',
mr_notes: './mr_notes/index.js',
terminal: './terminal/terminal_bundle.js',
two_factor_auth: './two_factor_auth.js',
common: './commons/index.js',
common_vue: './vue_shared/vue_resource_interceptor.js',
......@@ -63,10 +62,7 @@ function generateEntries() {
webpack_runtime: './webpack.js',
// EE-only
add_gitlab_slack_application: 'ee/add_gitlab_slack_application/index.js',
burndown_chart: 'ee/burndown_chart/index.js',
geo_nodes: 'ee/geo_nodes',
ldap_group_links: 'ee/groups/ldap_group_links.js',
service_desk: 'ee/projects/settings_service_desk/service_desk_bundle.js',
};
......
# Incoming email
GitLab has several features based on receiving incoming emails:
- [Reply by Email](reply_by_email.md): allow GitLab users to comment on issues
and merge requests by replying to notification emails.
- [New issue by email](../user/project/issues/create_new_issue.md#new-issue-via-email):
allow GitLab users to create a new issue by sending an email to a
user-specific email address.
- [New merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email):
allow GitLab users to create a new merge request by sending an email to a
user-specific email address.
- [Service Desk](../user/project/service_desk.md): provide e-mail support to
your customers through GitLab.
## Requirements
Handling incoming emails requires an [IMAP]-enabled email account. GitLab
requires one of the following three strategies:
- Email sub-addressing
- Dedicated email address
- Catch-all mailbox
Let's walk through each of these options.
**If your provider or server supports email sub-addressing, we recommend using it.
Most features (other than reply by email) only work with sub-addressing.**
[IMAP]: https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol
### Email sub-addressing
[Sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing) is
a feature where any email to `user+some_arbitrary_tag@example.com` will end up
in the mailbox for `user@example.com`, and is supported by providers such as
Gmail, Google Apps, Yahoo! Mail, Outlook.com and iCloud, as well as the
[Postfix mail server] which you can run on-premises.
[Postfix mail server]: reply_by_email_postfix_setup.md
### Dedicated email address
This solution is really simple to set up: you just have to create an email
address dedicated to receive your users' replies to GitLab notifications.
### Catch-all mailbox
A [catch-all mailbox](https://en.wikipedia.org/wiki/Catch-all) for a domain will
"catch all" the emails addressed to the domain that do not exist in the mail
server.
GitLab can be set up to allow users to comment on issues and merge requests by
replying to notification emails.
## Set it up
If you want to use Gmail / Google Apps for incoming emails, make sure you have
[IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018)
and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255)
or [turn-on 2-step validation](https://support.google.com/accounts/answer/185839)
and use [an application password](https://support.google.com/mail/answer/185833).
To set up a basic Postfix mail server with IMAP access on Ubuntu, follow the
[Postfix setup documentation](reply_by_email_postfix_setup.md).
### Security Concerns
**WARNING:** Be careful when choosing the domain used for receiving incoming
email.
For the sake of example, suppose your top-level company domain is `hooli.com`.
All employees in your company have an email address at that domain via Google
Apps, and your company's private Slack instance requires a valid `@hooli.com`
email address in order to sign up.
If you also host a public-facing GitLab instance at `hooli.com` and set your
incoming email domain to `hooli.com`, an attacker could abuse the "Create new
issue by email" or
"[Create new merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email)"
features by using a project's unique address as the email when signing up for
Slack, which would send a confirmation email, which would create a new issue or
merge request on the project owned by the attacker, allowing them to click the
confirmation link and validate their account on your company's private Slack
instance.
We recommend receiving incoming email on a subdomain, such as
`incoming.hooli.com`, and ensuring that you do not employ any services that
authenticate solely based on access to an email domain such as `*.hooli.com.`
Alternatively, use a dedicated domain for GitLab email communications such as
`hooli-gitlab.com`.
See GitLab issue [#30366](https://gitlab.com/gitlab-org/gitlab-ce/issues/30366)
for a real-world example of this exploit.
### Omnibus package installations
1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the
feature and fill in the details for your specific IMAP server and email account:
Configuration for Postfix mail server, assumes mailbox
incoming@gitlab.example.com
```ruby
gitlab_rails['incoming_email_enabled'] = true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "incoming"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "gitlab.example.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 143
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = false
# Whether the IMAP server uses StartTLS
gitlab_rails['incoming_email_start_tls'] = false
# The mailbox where incoming mail will end up. Usually "inbox".
gitlab_rails['incoming_email_mailbox_name'] = "inbox"
# The IDLE command timeout.
gitlab_rails['incoming_email_idle_timeout'] = 60
```
Configuration for Gmail / Google Apps, assumes mailbox
gitlab-incoming@gmail.com
```ruby
gitlab_rails['incoming_email_enabled'] = true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "imap.gmail.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 993
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = true
# Whether the IMAP server uses StartTLS
gitlab_rails['incoming_email_start_tls'] = false
# The mailbox where incoming mail will end up. Usually "inbox".
gitlab_rails['incoming_email_mailbox_name'] = "inbox"
# The IDLE command timeout.
gitlab_rails['incoming_email_idle_timeout'] = 60
```
Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes
mailbox incoming@exchange.example.com
```ruby
gitlab_rails['incoming_email_enabled'] = true
# The email address replies are sent to - Exchange does not support sub-addressing so %{key} is not used here
gitlab_rails['incoming_email_address'] = "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "exchange.example.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 993
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = true
```
1. Reconfigure GitLab for the changes to take effect:
```sh
sudo gitlab-ctl reconfigure
```
1. Verify that everything is configured correctly:
```sh
sudo gitlab-rake gitlab:incoming_email:check
```
1. Reply by email should now be working.
### Installations from source
1. Go to the GitLab installation directory:
```sh
cd /home/git/gitlab
```
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
and fill in the details for your specific IMAP server and email account:
```sh
sudo editor config/gitlab.yml
```
Configuration for Postfix mail server, assumes mailbox
incoming@gitlab.example.com
```yaml
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "incoming"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "gitlab.example.com"
# IMAP server port
port: 143
# Whether the IMAP server uses SSL
ssl: false
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
Configuration for Gmail / Google Apps, assumes mailbox
gitlab-incoming@gmail.com
```yaml
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "gitlab-incoming@gmail.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "imap.gmail.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes
mailbox incoming@exchange.example.com
```yaml
incoming_email:
enabled: true
# The email address replies are sent to - Exchange does not support sub-addressing so %{key} is not used here
address: "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
user: "incoming@ad-domain.example.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "exchange.example.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
```sh
sudo mkdir -p /etc/default
echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
```
1. Restart GitLab:
```sh
sudo service gitlab restart
```
1. Verify that everything is configured correctly:
```sh
sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
```
1. Reply by email should now be working.
......@@ -89,18 +89,24 @@ created in snippets, wikis, and repos.
- [Authentication/Authorization](../topics/authentication/index.md#gitlab-administrators): Enforce 2FA, configure external authentication with LDAP, SAML, CAS and additional Omniauth providers.
- **(Starter/Premium)** [Sync LDAP](auth/ldap-ee.md)
- **(Starter/Premium)** [Kerberos authentication](../integration/kerberos.md)
- [Reply by email](reply_by_email.md): Allow users to comment on issues and merge requests by replying to notification emails.
- [Postfix for Reply by email](reply_by_email_postfix_setup.md): Set up a basic Postfix mail
server with IMAP authentication on Ubuntu, to be used with Reply by email.
- **(Starter/Premium)** [Email users](../tools/email.md): Email GitLab users from within GitLab.
- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
- **(Starter/Premium)** [Audit logs and events](audit_events.md): View the changes made within the GitLab server.
- **(Premium)** [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance.
- [Reply by email](reply_by_email.md): Allow users to comment on issues and merge requests by replying to notification emails.
- [Postfix for Reply by email](reply_by_email_postfix_setup.md): Set up a basic Postfix mail
- [Incoming email](incoming_email.md): Configure incoming emails to allow
users to [reply by email], create [issues by email] and
[merge requests by email], and to enable [Service Desk].
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
emails.
server with IMAP authentication on Ubuntu, to be used with Reply by email.
- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
[reply by email]: reply_by_email.md
[issues by email]: ../user/project/issues/create_new_issue.md#new-issue-via-email
[merge requests by email]: ../user/project/merge_requests/index.md#create-new-merge-requests-by-email
[Service Desk]: ../user/project/service_desk.md
## Project settings
- [Container Registry](container_registry.md): Configure Container Registry with GitLab.
......
......@@ -5,33 +5,7 @@ replying to notification emails.
## Requirement
Reply by email requires an IMAP-enabled email account. GitLab allows you to use
three strategies for this feature:
- using email sub-addressing
- using a dedicated email address
- using a catch-all mailbox
### Email sub-addressing
**If your provider or server supports email sub-addressing, we recommend using it.
Some features (e.g. create new issue via email) only work with sub-addressing.**
[Sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing) is
a feature where any email to `user+some_arbitrary_tag@example.com` will end up
in the mailbox for `user@example.com`, and is supported by providers such as
Gmail, Google Apps, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix
mail server which you can run on-premises.
### Dedicated email address
This solution is really simple to set up: you just have to create an email
address dedicated to receive your users' replies to GitLab notifications.
### Catch-all mailbox
A [catch-all mailbox](https://en.wikipedia.org/wiki/Catch-all) for a domain will
"catch all" the emails addressed to the domain that do not exist in the mail
server.
Make sure [incoming email](incoming_email.md) is setup.
## How it works?
......@@ -65,329 +39,3 @@ the entity the notification was about (issue, merge request, commit...).
For more details about the `Message-ID`, `In-Reply-To`, and `References headers`,
please consult [RFC 5322](https://tools.ietf.org/html/rfc5322#section-3.6.4).
## Set it up
If you want to use Gmail / Google Apps with Reply by email, make sure you have
[IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018)
and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255)
or [turn-on 2-step validation](https://support.google.com/accounts/answer/185839)
and use [an application password](https://support.google.com/mail/answer/185833).
To set up a basic Postfix mail server with IMAP access on Ubuntu, follow the
[Postfix setup documentation](reply_by_email_postfix_setup.md).
### Security Concerns
**WARNING:** Be careful when choosing the domain used for receiving incoming
email.
For the sake of example, suppose your top-level company domain is `hooli.com`.
All employees in your company have an email address at that domain via Google
Apps, and your company's private Slack instance requires a valid `@hooli.com`
email address in order to sign up.
If you also host a public-facing GitLab instance at `hooli.com` and set your
incoming email domain to `hooli.com`, an attacker could abuse the "Create new
issue by email" or
"[Create new merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email)"
features by using a project's unique address as the email when signing up for
Slack, which would send a confirmation email, which would create a new issue or
merge request on the project owned by the attacker, allowing them to click the
confirmation link and validate their account on your company's private Slack
instance.
We recommend receiving incoming email on a subdomain, such as
`incoming.hooli.com`, and ensuring that you do not employ any services that
authenticate solely based on access to an email domain such as `*.hooli.com.`
Alternatively, use a dedicated domain for GitLab email communications such as
`hooli-gitlab.com`.
See GitLab issue [#30366](https://gitlab.com/gitlab-org/gitlab-ce/issues/30366)
for a real-world example of this exploit.
### Omnibus package installations
1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the
feature and fill in the details for your specific IMAP server and email account:
```ruby
# Configuration for Postfix mail server, assumes mailbox incoming@gitlab.example.com
gitlab_rails['incoming_email_enabled'] = true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "incoming"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "gitlab.example.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 143
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = false
# Whether the IMAP server uses StartTLS
gitlab_rails['incoming_email_start_tls'] = false
# The mailbox where incoming mail will end up. Usually "inbox".
gitlab_rails['incoming_email_mailbox_name'] = "inbox"
# The IDLE command timeout.
gitlab_rails['incoming_email_idle_timeout'] = 60
```
```ruby
# Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
gitlab_rails['incoming_email_enabled'] = true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "imap.gmail.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 993
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = true
# Whether the IMAP server uses StartTLS
gitlab_rails['incoming_email_start_tls'] = false
# The mailbox where incoming mail will end up. Usually "inbox".
gitlab_rails['incoming_email_mailbox_name'] = "inbox"
# The IDLE command timeout.
gitlab_rails['incoming_email_idle_timeout'] = 60
```
```ruby
# Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes mailbox incoming@exchange.example.com
gitlab_rails['incoming_email_enabled'] = true
# The email address replies are sent to - Exchange does not support sub-addressing so %{key} is not used here
gitlab_rails['incoming_email_address'] = "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "exchange.example.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 993
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = true
```
1. Reconfigure GitLab for the changes to take effect:
```sh
sudo gitlab-ctl reconfigure
```
1. Verify that everything is configured correctly:
```sh
sudo gitlab-rake gitlab:incoming_email:check
```
1. Reply by email should now be working.
### Installations from source
1. Go to the GitLab installation directory:
```sh
cd /home/git/gitlab
```
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
and fill in the details for your specific IMAP server and email account:
```sh
sudo editor config/gitlab.yml
```
```yaml
# Configuration for Postfix mail server, assumes mailbox incoming@gitlab.example.com
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "incoming"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "gitlab.example.com"
# IMAP server port
port: 143
# Whether the IMAP server uses SSL
ssl: false
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
```yaml
# Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "gitlab-incoming@gmail.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "imap.gmail.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
```yaml
# Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes mailbox incoming@exchange.example.com
incoming_email:
enabled: true
# The email address replies are sent to - Exchange does not support sub-addressing so %{key} is not used here
address: "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
user: "incoming@ad-domain.example.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "exchange.example.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
```sh
sudo mkdir -p /etc/default
echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
```
1. Restart GitLab:
```sh
sudo service gitlab restart
```
1. Verify that everything is configured correctly:
```sh
sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
```
1. Reply by email should now be working.
### Development
1. Go to the GitLab installation directory.
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and fill in the details for your specific IMAP server and email account:
```yaml
# Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "gitlab-incoming@gmail.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "imap.gmail.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
1. Uncomment the `mail_room` line in your `Procfile`:
```yaml
mail_room: bundle exec mail_room -q -c config/mail_room.yml
```
1. Restart GitLab:
```sh
bundle exec foreman start
```
1. Verify that everything is configured correctly:
```sh
bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
```
1. Reply by email should now be working.
# Set up Postfix for Reply by email
# Set up Postfix for incoming email
This document will take you through the steps of setting up a basic Postfix mail
server with IMAP authentication on Ubuntu, to be used with [Reply by email].
server with IMAP authentication on Ubuntu, to be used with [incoming email].
The instructions make the assumption that you will be using the email address `incoming@gitlab.example.com`, that is, username `incoming` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets.
......@@ -177,12 +177,12 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
```sh
sudo apt-get install courier-imap
```
And start `imapd`:
```sh
imapd start
```
1. The courier-authdaemon isn't started after installation. Without it, imap authentication will fail:
```sh
sudo service courier-authdaemon start
......@@ -329,10 +329,10 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
## Done!
If all the tests were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./reply_by_email.md) guide to configure GitLab.
If all the tests were successful, Postfix is all set up and ready to receive email! Continue with the [incoming email] guide to configure GitLab.
---
_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._
[reply by email]: reply_by_email.md
[incoming email]: incoming_email.md
......@@ -263,20 +263,20 @@ Parameters:
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"state" : "active",
"web_url" : "https://gitlab.example.com/root",
"avatar_url" : null,
"username" : "root",
"id" : 1,
"name" : "Administrator"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"state" : "active",
"web_url" : "https://gitlab.example.com/root",
"avatar_url" : null,
"username" : "root",
"id" : 1,
"name" : "Administrator"
},
"source_project_id": 2,
"target_project_id": 3,
......@@ -313,6 +313,27 @@ Parameters:
"human_total_time_spent": null
},
"approvals_before_merge": null
},
"closed_at": "2018-01-19T14:36:11.086Z",
"latest_build_started_at": null,
"latest_build_finished_at": null,
"first_deployed_to_production_at": null,
"pipeline": {
"id": 8,
"ref": "master",
"sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
"status": "created"
},
"merged_by": null,
"merged_at": null,
"closed_by": {
"state" : "active",
"web_url" : "https://gitlab.example.com/root",
"avatar_url" : null,
"username" : "root",
"id" : 1,
"name" : "Administrator"
}
}
```
......
......@@ -18,6 +18,68 @@ See the [Rails guides] for more info.
[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/spec/mailers/previews
[Rails guides]: http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails
## Incoming email
1. Go to the GitLab installation directory.
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the
feature and fill in the details for your specific IMAP server and email
account:
Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
```yaml
incoming_email:
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
user: "gitlab-incoming@gmail.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "imap.gmail.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
```
As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
1. Uncomment the `mail_room` line in your `Procfile`:
```yaml
mail_room: bundle exec mail_room -q -c config/mail_room.yml
```
1. Restart GitLab:
```sh
bundle exec foreman start
```
1. Verify that everything is configured correctly:
```sh
bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
```
1. Reply by email should now be working.
---
[Return to Development documentation](README.md)
......@@ -37,6 +37,28 @@ It opens a new issue for that project labeled after its respective list.
![From the issue board](img/new_issue_from_issue_board.png)
## New issue via email
*This feature needs [incoming email](../../../administration/incoming_email.md)
to be configured by a GitLab administrator to be available for CE/EE users, and
it's available on GitLab.com.*
At the bottom of a project's issue page, click
**Email a new issue to this project**, and you will find an email address
which belongs to you. You could add this address to your contact.
This is a private email address, generated just for you.
**Keep it to yourself** as anyone who gets ahold of it can create issues or
merge requests as if they were you. You can add this address to your contact
list for easy access.
Sending an email to this address will create a new issue on your behalf for
this project, where the email subject becomes the issue title, and the email
body becomes the issue description. [Markdown] and [quick actions] are
supported.
![Bottom of a project issues page](img/new_issue_from_email.png)
## New issue via Service Desk
Enable [Service Desk](../service_desk.md) to your project and offer email support.
......@@ -60,5 +82,5 @@ create issues for the same project.
![Create issue from group-level issue tracker](img/create_issue_from_group_level_issue_tracker.png)
[Markdown]: ../../markdown.md
[quick actions]: ../quick_actions.md
......@@ -142,6 +142,10 @@ those conflicts in the GitLab UI.
## Create new merge requests by email
*This feature needs [incoming email](../../../administration/incoming_email.md)
to be configured by a GitLab administrator to be available for CE/EE users, and
it's available on GitLab.com.*
You can create a new merge request by sending an email to a user-specific email
address. The address can be obtained on the merge requests page by clicking on
a **Email a new merge request to this project** button. The subject will be
......
......@@ -53,7 +53,7 @@ Service Desk is enabled on GitLab.com. If you're a
[Silver subscriber](https://about.gitlab.com/gitlab-com/),
you can skip the step 1 below; you only need to enable it per project.
1. [Set up reply by email][reply-by-email] for the GitLab instance. This must
1. [Set up incoming email][incoming-email] for the GitLab instance. This must
support [email sub-addressing][email-sub-addressing].
2. Navigate to your project's **Settings** and scroll down to the **Service Desk**
section.
......@@ -119,7 +119,7 @@ does not count toward the license limit count.
[ee-149]: https://gitlab.com/gitlab-org/gitlab-ee/issues/149 "Service Desk with email"
[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition landing page"
[eep-9.1]: https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#service-desk-eep
[reply-by-email]: ../../administration/reply_by_email.md#set-it-up
[incoming-email]: ../../administration/incoming_email.md#set-it-up
[email-sub-addressing]: ../../administration/reply_by_email.md#email-sub-addressing
[confidential]: ./issues/confidential_issues.md "Confidential issues"
[akismet]: ../../integration/akismet.md
import Vue from 'vue';
import AddGitlabSlackApplication from './components/add_gitlab_slack_application.vue';
function mountAddGitlabSlackApplication() {
export default () => {
const el = document.getElementById('js-add-gitlab-slack-application-entry-point');
if (!el) return;
......@@ -23,8 +23,4 @@ function mountAddGitlabSlackApplication() {
docsPath: initialData.docs_path,
},
}).$mount(el);
}
document.addEventListener('DOMContentLoaded', mountAddGitlabSlackApplication);
export default mountAddGitlabSlackApplication;
};
import Cookies from 'js-cookie';
import BurndownChart from './burndown_chart';
$(() => {
export default () => {
// handle hint dismissal
const hint = $('.burndown-hint');
hint.on('click', '.dismiss-icon', () => {
......@@ -47,4 +47,4 @@ $(() => {
window.addEventListener('resize', () => chart.animateResize(1));
$(document).on('click', '.js-sidebar-toggle', () => chart.animateResize(2));
}
});
};
document.addEventListener('DOMContentLoaded', () => {
export default () => {
const showGroupLink = () => {
const $cnLink = $('.cn-link');
const $filterLink = $('.filter-link');
......@@ -12,4 +12,4 @@ document.addEventListener('DOMContentLoaded', () => {
$('input[name="sync_method"]').on('change', showGroupLink);
showGroupLink();
});
};
import initLDAPGroupsSelect from 'ee/ldap_groups_select';
import initLDAPGroupLinks from 'ee/groups/ldap_group_links';
document.addEventListener('DOMContentLoaded', initLDAPGroupsSelect);
document.addEventListener('DOMContentLoaded', () => {
initLDAPGroupsSelect();
initLDAPGroupLinks();
});
import '~/pages/admin/groups/new/index';
import initLDAPGroupLinks from 'ee/groups/ldap_group_links';
document.addEventListener('DOMContentLoaded', initLDAPGroupLinks);
import initLDAPGroupsSelect from 'ee/ldap_groups_select';
import initLDAPGroupLinks from 'ee/groups/ldap_group_links';
document.addEventListener('DOMContentLoaded', initLDAPGroupsSelect);
document.addEventListener('DOMContentLoaded', () => {
initLDAPGroupsSelect();
initLDAPGroupLinks();
});
import mountAddGitlabSlackApplication from 'ee/add_gitlab_slack_application';
document.addEventListener('DOMContentLoaded', () => mountAddGitlabSlackApplication());
import '~/pages/projects/milestones/show/index';
import UserCallout from '~/user_callout';
import initBurndownChart from 'ee/burndown_chart';
document.addEventListener('DOMContentLoaded', () => new UserCallout());
document.addEventListener('DOMContentLoaded', () => {
new UserCallout(); // eslint-disable-line no-new
initBurndownChart();
});
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'ldap_group_links'
%section.ldap-group-links
= form_for [group, LdapGroupLink.new], html: { class: 'form-horizontal' } do |f|
.form-holder
......
= webpack_bundle_tag 'add_gitlab_slack_application'
-# haml-lint:disable InlineJavaScript
%script#js-add-gitlab-slack-application-entry-data{ type: "application/json" }
= add_to_slack_data(@projects)
......
......@@ -3,9 +3,6 @@
- burndown = local_assigns[:burndown]
- warning = data_warning_for(burndown)
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'burndown_chart'
= warning
- if can_generate_chart?(burndown)
......
---
title: File uploads in remote storage now support project renaming.
merge_request: 4597
author:
type: fixed
......@@ -483,6 +483,10 @@ module API
expose :id
end
class PipelineBasic < Grape::Entity
expose :id, :sha, :ref, :status
end
class MergeRequestSimple < ProjectEntity
expose :title
expose :web_url do |merge_request, options|
......@@ -548,6 +552,42 @@ module API
expose :changes_count do |merge_request, _options|
merge_request.merge_request_diff.real_size
end
expose :merged_by, using: Entities::UserBasic do |merge_request, _options|
merge_request.metrics&.merged_by
end
expose :merged_at do |merge_request, _options|
merge_request.metrics&.merged_at
end
expose :closed_by, using: Entities::UserBasic do |merge_request, _options|
merge_request.metrics&.latest_closed_by
end
expose :closed_at do |merge_request, _options|
merge_request.metrics&.latest_closed_at
end
expose :latest_build_started_at, if: -> (_, options) { build_available?(options) } do |merge_request, _options|
merge_request.metrics&.latest_build_started_at
end
expose :latest_build_finished_at, if: -> (_, options) { build_available?(options) } do |merge_request, _options|
merge_request.metrics&.latest_build_finished_at
end
expose :first_deployed_to_production_at, if: -> (_, options) { build_available?(options) } do |merge_request, _options|
merge_request.metrics&.first_deployed_to_production_at
end
expose :pipeline, using: Entities::PipelineBasic, if: -> (_, options) { build_available?(options) } do |merge_request, _options|
merge_request.metrics&.pipeline
end
def build_available?(options)
options[:project]&.feature_available?(:builds, options[:current_user])
end
end
class MergeRequestChanges < MergeRequest
......@@ -911,10 +951,6 @@ module API
expose :filename, :size
end
class PipelineBasic < Grape::Entity
expose :id, :sha, :ref, :status
end
class JobBasic < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
......
......@@ -234,7 +234,7 @@ module API
get ':id/merge_requests/:merge_request_iid/changes' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user, project: user_project
end
desc 'Get the merge request pipelines' do
......
......@@ -9,6 +9,7 @@ describe API::MergeRequests do
let(:non_member) { create(:user) }
let!(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let(:pipeline) { create(:ci_empty_pipeline) }
let(:milestone1) { create(:milestone, title: '0.9', project: project) }
let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
let!(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
......@@ -501,6 +502,45 @@ describe API::MergeRequests do
expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size)
end
context 'merge_request_metrics' do
before do
merge_request.metrics.update!(merged_by: user,
latest_closed_by: user,
latest_closed_at: 1.hour.ago,
merged_at: 2.hours.ago,
pipeline: pipeline,
latest_build_started_at: 3.hours.ago,
latest_build_finished_at: 1.hour.ago,
first_deployed_to_production_at: 3.minutes.ago)
end
it 'has fields from merge request metrics' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
expect(json_response).to include('merged_by',
'merged_at',
'closed_by',
'closed_at',
'latest_build_started_at',
'latest_build_finished_at',
'first_deployed_to_production_at',
'pipeline')
end
it 'returns correct values' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.reload.iid}", user)
expect(json_response['merged_by']['id']).to eq(merge_request.metrics.merged_by_id)
expect(Time.parse json_response['merged_at']).to be_like_time(merge_request.metrics.merged_at)
expect(json_response['closed_by']['id']).to eq(merge_request.metrics.latest_closed_by_id)
expect(Time.parse json_response['closed_at']).to be_like_time(merge_request.metrics.latest_closed_at)
expect(json_response['pipeline']['id']).to eq(merge_request.metrics.pipeline_id)
expect(Time.parse json_response['latest_build_started_at']).to be_like_time(merge_request.metrics.latest_build_started_at)
expect(Time.parse json_response['latest_build_finished_at']).to be_like_time(merge_request.metrics.latest_build_finished_at)
expect(Time.parse json_response['first_deployed_to_production_at']).to be_like_time(merge_request.metrics.first_deployed_to_production_at)
end
end
it "returns a 404 error if merge_request_iid not found" do
get api("/projects/#{project.id}/merge_requests/999", user)
expect(response).to have_gitlab_http_status(404)
......
......@@ -11,34 +11,30 @@ describe FileUploader do
shared_examples 'builds correct legacy storage paths' do
include_examples 'builds correct paths',
store_dir: %r{awesome/project/\h+},
upload_path: %r{\h+/<filename>},
absolute_path: %r{#{described_class.root}/awesome/project/secret/foo.jpg}
end
shared_examples 'uses hashed storage' do
context 'when rolled out attachments' do
let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
context 'legacy storage' do
it_behaves_like 'builds correct legacy storage paths'
before do
allow(project).to receive(:disk_path).and_return('ca/fe/fe/ed')
end
context 'uses hashed storage' do
context 'when rolled out attachments' do
let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
it_behaves_like 'builds correct paths',
store_dir: %r{ca/fe/fe/ed/\h+},
absolute_path: %r{#{described_class.root}/ca/fe/fe/ed/secret/foo.jpg}
end
include_examples 'builds correct paths',
store_dir: %r{@hashed/\h{2}/\h{2}/\h+},
upload_path: %r{\h+/<filename>}
end
context 'when only repositories are rolled out' do
let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) }
context 'when only repositories are rolled out' do
let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) }
it_behaves_like 'builds correct legacy storage paths'
it_behaves_like 'builds correct legacy storage paths'
end
end
end
context 'legacy storage' do
it_behaves_like 'builds correct legacy storage paths'
include_examples 'uses hashed storage'
end
context 'object store is remote' do
before do
stub_uploads_object_storage
......@@ -46,8 +42,10 @@ describe FileUploader do
include_context 'with storage', described_class::Store::REMOTE
it_behaves_like 'builds correct legacy storage paths'
include_examples 'uses hashed storage'
# always use hashed storage path for remote uploads
it_behaves_like 'builds correct paths',
store_dir: %r{@hashed/\h{2}/\h{2}/\h+},
upload_path: %r{@hashed/\h{2}/\h{2}/\h+/\h+/<filename>}
end
describe 'initialize' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment