From f098e6d3d2c8eaaec0a228c8a3ae01f770e15dd2 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 6 Apr 2020 03:09:23 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/graphql/types/base_field.rb | 1 + app/graphql/types/commit_type.rb | 2 +- app/graphql/types/grafana_integration_type.rb | 2 +- app/graphql/types/merge_request_type.rb | 2 +- app/graphql/types/project_type.rb | 6 +- ...e-deprecated-strings-from-descriptions.yml | 5 + ...rmalize-signature-protocol-attachments.yml | 5 + doc/administration/high_availability/nfs.md | 62 ++-- .../graphql/reference/gitlab_schema.graphql | 12 +- doc/api/graphql/reference/gitlab_schema.json | 12 +- doc/api/graphql/reference/index.md | 12 +- doc/development/api_graphql_styleguide.md | 86 +++-- doc/user/project/issues/csv_export.md | 2 +- .../project/issues/img/csv_export_button.png | Bin 7383 -> 0 bytes .../issues/img/csv_export_button_v12_9.png | Bin 0 -> 34524 bytes lib/gitlab/email/attachment_uploader.rb | 15 +- ...ply_signed_smime_mixed_protocol_prefix.eml | 294 ++++++++++++++++++ spec/graphql/types/base_field_spec.rb | 23 +- .../gitlab/email/attachment_uploader_spec.rb | 15 + spec/lib/gitlab/graphql/docs/renderer_spec.rb | 4 +- 20 files changed, 473 insertions(+), 87 deletions(-) create mode 100644 changelogs/unreleased/211620-remove-deprecated-strings-from-descriptions.yml create mode 100644 changelogs/unreleased/fix-normalize-signature-protocol-attachments.yml delete mode 100644 doc/user/project/issues/img/csv_export_button.png create mode 100644 doc/user/project/issues/img/csv_export_button_v12_9.png create mode 100644 spec/fixtures/emails/valid_reply_signed_smime_mixed_protocol_prefix.eml diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 7e9d6392705..c8ec4b3f897 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -65,6 +65,7 @@ module Types raise ArgumentError, 'Please provide a `milestone` within `deprecated`' unless milestone raise ArgumentError, 'Please provide a `reason` within `deprecated`' unless reason + raise ArgumentError, '`milestone` must be a `String`' unless milestone.is_a?(String) deprecated_in = "Deprecated in #{milestone}" kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}" diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb index 437da3bb585..aaf2dfd8488 100644 --- a/app/graphql/types/commit_type.rb +++ b/app/graphql/types/commit_type.rb @@ -44,7 +44,7 @@ module Types field :latest_pipeline, type: Types::Ci::PipelineType, null: true, - deprecated: { reason: 'Use `pipelines`', milestone: 12.5 }, + deprecated: { reason: 'Use `pipelines`', milestone: '12.5' }, description: 'Latest pipeline of the commit', resolver: Resolvers::CommitPipelinesResolver.last end diff --git a/app/graphql/types/grafana_integration_type.rb b/app/graphql/types/grafana_integration_type.rb index 71018f6ce0a..c0582b266ab 100644 --- a/app/graphql/types/grafana_integration_type.rb +++ b/app/graphql/types/grafana_integration_type.rb @@ -18,7 +18,7 @@ module Types description: 'Timestamp of the issue\'s last activity' field :token, GraphQL::STRING_TYPE, null: false, - deprecated: { reason: 'Plain text token has been masked for security reasons', milestone: 12.7 }, + deprecated: { reason: 'Plain text token has been masked for security reasons', milestone: '12.7' }, description: 'API token for the Grafana integration' def token diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 8cb439cb465..8f6b742a93c 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -75,7 +75,7 @@ module Types field :rebase_in_progress, GraphQL::BOOLEAN_TYPE, method: :rebase_in_progress?, null: false, calls_gitaly: true, description: 'Indicates if there is a rebase currently in progress for the merge request' field :merge_commit_message, GraphQL::STRING_TYPE, method: :default_merge_commit_message, null: true, - deprecated: { reason: 'Use `defaultMergeCommitMessage`', milestone: 11.8 }, + deprecated: { reason: 'Use `defaultMergeCommitMessage`', milestone: '11.8' }, description: 'Default merge commit message of the merge request' field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true, description: 'Default merge commit message of the merge request' diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index b52266bc477..d82feffe441 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -54,7 +54,7 @@ module Types field :container_registry_enabled, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if the project stores Docker container images in a container registry' field :shared_runners_enabled, GraphQL::BOOLEAN_TYPE, null: true, - description: 'Indicates if shared runners are enabled on the project' + description: 'Indicates if Shared Runners are enabled for the project' field :lfs_enabled, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if the project has Large File Storage (LFS) enabled' field :merge_requests_ff_only_enabled, GraphQL::BOOLEAN_TYPE, null: true, @@ -68,14 +68,14 @@ module Types %i[issues merge_requests wiki snippets].each do |feature| field "#{feature}_enabled", GraphQL::BOOLEAN_TYPE, null: true, - description: "(deprecated) Does this project have #{feature} enabled?. Use `#{feature}_access_level` instead", + description: "Indicates if #{feature.to_s.titleize.pluralize} are enabled for the current user", resolve: -> (project, args, ctx) do project.feature_available?(feature, ctx[:current_user]) end end field :jobs_enabled, GraphQL::BOOLEAN_TYPE, null: true, - description: '(deprecated) Enable jobs for this project. Use `builds_access_level` instead', + description: 'Indicates if CI/CD pipeline jobs are enabled for the current user', resolve: -> (project, args, ctx) do project.feature_available?(:builds, ctx[:current_user]) end diff --git a/changelogs/unreleased/211620-remove-deprecated-strings-from-descriptions.yml b/changelogs/unreleased/211620-remove-deprecated-strings-from-descriptions.yml new file mode 100644 index 00000000000..71ab1df09f9 --- /dev/null +++ b/changelogs/unreleased/211620-remove-deprecated-strings-from-descriptions.yml @@ -0,0 +1,5 @@ +--- +title: Cleanup the descriptions of some fields of GraphQL ProjectType +merge_request: 28735 +author: +type: changed diff --git a/changelogs/unreleased/fix-normalize-signature-protocol-attachments.yml b/changelogs/unreleased/fix-normalize-signature-protocol-attachments.yml new file mode 100644 index 00000000000..aa148c975b6 --- /dev/null +++ b/changelogs/unreleased/fix-normalize-signature-protocol-attachments.yml @@ -0,0 +1,5 @@ +--- +title: Normalize signature mime types when filtering attachments in emails +merge_request: 28865 +author: Diego Louzán +type: fixed diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md index 9b83ae581b7..192434f7907 100644 --- a/doc/administration/high_availability/nfs.md +++ b/doc/administration/high_availability/nfs.md @@ -52,6 +52,36 @@ management between systems: - [NetApp instructions](https://library.netapp.com/ecmdocs/ECMP1401220/html/GUID-24367A9F-E17B-4725-ADC1-02D86F56F78E.html) - For non-NetApp devices, disable NFSv4 `idmapping` by performing opposite of [enable NFSv4 idmapper](https://wiki.archlinux.org/index.php/NFS#Enabling_NFSv4_idmapping) +### Disable NFS server delegation + +We recommend that all NFS users disable the NFS server delegation feature. This +is to avoid a [Linux kernel bug](https://bugzilla.redhat.com/show_bug.cgi?id=1552203) +which causes NFS clients to slow precipitously due to +[excessive network traffic from numerous `TEST_STATEID` NFS messages](https://gitlab.com/gitlab-org/gitlab-foss/issues/52017). + +To disable NFS server delegation, do the following: + +1. On the NFS server, run: + + ```shell + echo 0 > /proc/sys/fs/leases-enable + sysctl -w fs.leases-enable=0 + ``` + +1. Restart the NFS server process. For example, on CentOS run `service nfs restart`. + +#### Important notes + +The kernel bug may be fixed in +[more recent kernels with this commit](https://github.om/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140). + +Red Hat Enterprise 7 [shipped a kernel update](https://access.redhat.com/errata/RHSA-2019:2029) +on August 6, 2019 that may also have resolved this problem. + +You may not need to disable NFS server delegation if you know you are using a version of +the Linux kernel that has been fixed. That said, GitLab still encourages instance +administrators to keep NFS server delegation disabled. + ### Improving NFS performance with GitLab #### Improving NFS performance with Unicorn @@ -78,33 +108,7 @@ If the Rugged feature flag is explicitly set to either true or false, GitLab wil ### Known issues -On some customer systems, we have seen NFS clients slow precipitously due to -[excessive network traffic from numerous `TEST_STATEID` NFS -messages](https://gitlab.com/gitlab-org/gitlab-foss/issues/52017). This is -likely due to a [Linux kernel -bug](https://bugzilla.redhat.com/show_bug.cgi?id=1552203) that may be fixed in -[more recent kernels with this -commit](https://github.com/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140). - -NOTE: **Note** Red Hat Enterprise 7 [shipped a kernel -update](https://access.redhat.com/errata/RHSA-2019:2029) on August 6, -2019 that may have resolved this problem. The following instructions may -not be needed if the latest kernel is updated properly. - -GitLab recommends all NFS users disable the NFS server -delegation feature. To disable NFS server delegations -on an Linux NFS server, do the following: - -1. On the NFS server, run: - - ```shell - echo 0 > /proc/sys/fs/leases-enable - sysctl -w fs.leases-enable=0 - ``` - -1. Restart the NFS server process. For example, on CentOS run `service nfs restart`. - -## Avoid using AWS's Elastic File System (EFS) +#### Avoid using AWS's Elastic File System (EFS) GitLab strongly recommends against using AWS Elastic File System (EFS). Our support team will not be able to assist on performance issues related to @@ -120,12 +124,12 @@ stored on a local volume. For more details on another person's experience with EFS, see this [Commit Brooklyn 2019 video](https://youtu.be/K6OS8WodRBQ?t=313). -## Avoid using CephFS and GlusterFS +#### Avoid using CephFS and GlusterFS GitLab strongly recommends against using CephFS and GlusterFS. These distributed file systems are not well-suited for GitLab's input/output access patterns because Git uses many small files and access times and file locking times to propagate will make Git activity very slow. -## Avoid using PostgreSQL with NFS +#### Avoid using PostgreSQL with NFS GitLab strongly recommends against running your PostgreSQL database across NFS. The GitLab support team will not be able to assist on performance issues related to diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 79e9c25c9a5..50d5eeaf9ab 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -6093,7 +6093,7 @@ type Project { ): IssueConnection """ - (deprecated) Does this project have issues enabled?. Use `issues_access_level` instead + Indicates if Issues are enabled for the current user """ issuesEnabled: Boolean @@ -6128,7 +6128,7 @@ type Project { ): JiraImportConnection """ - (deprecated) Enable jobs for this project. Use `builds_access_level` instead + Indicates if CI/CD pipeline jobs are enabled for the current user """ jobsEnabled: Boolean @@ -6193,7 +6193,7 @@ type Project { ): MergeRequestConnection """ - (deprecated) Does this project have merge_requests enabled?. Use `merge_requests_access_level` instead + Indicates if Merge Requests are enabled for the current user """ mergeRequestsEnabled: Boolean @@ -6406,7 +6406,7 @@ type Project { serviceDeskEnabled: Boolean """ - Indicates if shared runners are enabled on the project + Indicates if Shared Runners are enabled for the project """ sharedRunnersEnabled: Boolean @@ -6446,7 +6446,7 @@ type Project { ): SnippetConnection """ - (deprecated) Does this project have snippets enabled?. Use `snippets_access_level` instead + Indicates if Snippets are enabled for the current user """ snippetsEnabled: Boolean @@ -6542,7 +6542,7 @@ type Project { webUrl: String """ - (deprecated) Does this project have wiki enabled?. Use `wiki_access_level` instead + Indicates if Wikis are enabled for the current user """ wikiEnabled: Boolean } diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 67fc5738a5e..c7281fcc638 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -18344,7 +18344,7 @@ }, { "name": "issuesEnabled", - "description": "(deprecated) Does this project have issues enabled?. Use `issues_access_level` instead", + "description": "Indicates if Issues are enabled for the current user", "args": [ ], @@ -18425,7 +18425,7 @@ }, { "name": "jobsEnabled", - "description": "(deprecated) Enable jobs for this project. Use `builds_access_level` instead", + "description": "Indicates if CI/CD pipeline jobs are enabled for the current user", "args": [ ], @@ -18589,7 +18589,7 @@ }, { "name": "mergeRequestsEnabled", - "description": "(deprecated) Does this project have merge_requests enabled?. Use `merge_requests_access_level` instead", + "description": "Indicates if Merge Requests are enabled for the current user", "args": [ ], @@ -19125,7 +19125,7 @@ }, { "name": "sharedRunnersEnabled", - "description": "Indicates if shared runners are enabled on the project", + "description": "Indicates if Shared Runners are enabled for the project", "args": [ ], @@ -19220,7 +19220,7 @@ }, { "name": "snippetsEnabled", - "description": "(deprecated) Does this project have snippets enabled?. Use `snippets_access_level` instead", + "description": "Indicates if Snippets are enabled for the current user", "args": [ ], @@ -19493,7 +19493,7 @@ }, { "name": "wikiEnabled", - "description": "(deprecated) Does this project have wiki enabled?. Use `wiki_access_level` instead", + "description": "Indicates if Wikis are enabled for the current user", "args": [ ], diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 2a1c501e785..d1ddcb6435b 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -901,13 +901,13 @@ Information about pagination in a connection. | `id` | ID! | ID of the project | | `importStatus` | String | Status of import background job of the project | | `issue` | Issue | A single issue of the project | -| `issuesEnabled` | Boolean | (deprecated) Does this project have issues enabled?. Use `issues_access_level` instead | +| `issuesEnabled` | Boolean | Indicates if Issues are enabled for the current user | | `jiraImportStatus` | String | Status of Jira import background job of the project | -| `jobsEnabled` | Boolean | (deprecated) Enable jobs for this project. Use `builds_access_level` instead | +| `jobsEnabled` | Boolean | Indicates if CI/CD pipeline jobs are enabled for the current user | | `lastActivityAt` | Time | Timestamp of the project last activity | | `lfsEnabled` | Boolean | Indicates if the project has Large File Storage (LFS) enabled | | `mergeRequest` | MergeRequest | A single merge request of the project | -| `mergeRequestsEnabled` | Boolean | (deprecated) Does this project have merge_requests enabled?. Use `merge_requests_access_level` instead | +| `mergeRequestsEnabled` | Boolean | Indicates if Merge Requests are enabled for the current user | | `mergeRequestsFfOnlyEnabled` | Boolean | Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. | | `name` | String! | Name of the project (without namespace) | | `nameWithNamespace` | String! | Full name of the project with its namespace | @@ -927,8 +927,8 @@ Information about pagination in a connection. | `sentryErrors` | SentryErrorCollection | Paginated collection of Sentry errors on the project | | `serviceDeskAddress` | String | E-mail address of the service desk. | | `serviceDeskEnabled` | Boolean | Indicates if the project has service desk enabled. | -| `sharedRunnersEnabled` | Boolean | Indicates if shared runners are enabled on the project | -| `snippetsEnabled` | Boolean | (deprecated) Does this project have snippets enabled?. Use `snippets_access_level` instead | +| `sharedRunnersEnabled` | Boolean | Indicates if Shared Runners are enabled for the project | +| `snippetsEnabled` | Boolean | Indicates if Snippets are enabled for the current user | | `sshUrlToRepo` | String | URL to connect to the project via SSH | | `starCount` | Int! | Number of times the project has been starred | | `statistics` | ProjectStatistics | Statistics of the project | @@ -938,7 +938,7 @@ Information about pagination in a connection. | `visibility` | String | Visibility of the project | | `vulnerabilitySeveritiesCount` | VulnerabilitySeveritiesCount | Counts for each severity of vulnerability of the project. Available only when feature flag `first_class_vulnerabilities` is enabled | | `webUrl` | String | Web URL of the project | -| `wikiEnabled` | Boolean | (deprecated) Does this project have wiki enabled?. Use `wiki_access_level` instead | +| `wikiEnabled` | Boolean | Indicates if Wikis are enabled for the current user | ## ProjectPermissions diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index 3c475639834..7fd158aec04 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -45,8 +45,8 @@ For example, `app/graphql/types/issue_type.rb`: ```ruby graphql_name 'Issue' -field :iid, GraphQL::ID_TYPE, null: false -field :title, GraphQL::STRING_TYPE, null: false +field :iid, GraphQL::ID_TYPE, null: true +field :title, GraphQL::STRING_TYPE, null: true # we also have a method here that we've defined, that extends `field` markdown_field :title_html, null: true @@ -258,30 +258,78 @@ end ## Feature flags -Features controlled by feature flags often provide GraphQL functionality. When a feature -is enabled or disabled by a feature flag, the related GraphQL functionality should also -be enabled or disabled. +Developers can add [feature flags](../development/feature_flags/index.md) to GraphQL +fields in the following ways: -Fields can be put behind a feature flag so they can conditionally return the value for -the field depending on if the feature has been enabled or not. +- Add the `feature_flag` property to a field. This will allow the field to be _hidden_ + from the GraphQL schema when the flag is disabled. +- Toggle the return value when resolving the field. -GraphQL feature flags use the common -[GitLab feature flag](../development/feature_flags.md) system, and can be added to a -field using the `feature_flag` property. +You can refer to these guidelines to decide which approach to use: -For example: +- If your field is experimental, and its name or type is subject to + change, use the `feature_flag` property. +- If your field is stable and its definition will not change, even after the flag is + removed, toggle the return value of the field instead. Note that + [all fields should be nullable](#nullable-fields) anyway. + +### `feature_flag` property + +The `feature_flag` property allows you to toggle the field's +[visibility](https://graphql-ruby.org/authorization/visibility.html) +within the GraphQL schema. This will remove the field from the schema +when the flag is disabled. + +A description is [appended](https://gitlab.com/gitlab-org/gitlab/-/blob/497b556/app/graphql/types/base_field.rb#L44-53) +to the field indicating that it is behind a feature flag. + +CAUTION: **Caution:** +If a client queries for the field when the feature flag is disabled, the query will +fail. Consider this when toggling the visibility of the feature on or off on +production. + +The `feature_flag` property does not allow the use of +[feature gates based on actors](../development/feature_flags/development.md). +This means that the feature flag cannot be toggled only for particular +projects, groups, or users, but instead can only be toggled globally for +everyone. + +Example: ```ruby field :test_field, type: GraphQL::STRING_TYPE, - null: false, + null: true, description: 'Some test field', - feature_flag: :some_feature_flag + feature_flag: :my_feature_flag ``` -In the above example, the `test_field` field will only be returned if -the `some_feature_flag` feature flag is enabled. +### Toggle the value of a field -If the feature flag is not enabled, an error will be returned saying the field does not exist. +This method of using feature flags for fields is to toggle the +return value of the field. This can be done in the resolver, in the +type, or even in a model method, depending on your preference and +situation. + +When applying a feature flag to toggle the value of a field, the +`description` of the field must: + +- State that the value of the field can be toggled by a feature flag. +- Name the feature flag. +- State what the field will return when the feature flag is disabled (or + enabled, if more appropriate). + +Example: + +```ruby +field :foo, GraphQL::STRING_TYPE, + null: true, + description: 'Some test field. Will always return `null`' \ + 'if `my_feature_flag` feature flag is disabled' + +def foo + object.foo unless Feature.enabled?(:my_feature_flag, object) +end +``` ## Deprecating fields @@ -301,7 +349,7 @@ Example: ```ruby field :token, GraphQL::STRING_TYPE, null: true, - deprecated: { reason: 'Login via token has been removed', milestone: 10.0 }, + deprecated: { reason: 'Login via token has been removed', milestone: '10.0' }, description: 'Token for login' ``` @@ -321,7 +369,7 @@ Example: ```ruby field :designs, ::Types::DesignManagement::DesignCollectionType, null: true, - deprecated: { reason: 'Use `designCollection`', milestone: 10.0 }, + deprecated: { reason: 'Use `designCollection`', milestone: '10.0' }, description: 'The designs associated with this issue', ``` @@ -741,7 +789,7 @@ and handles time inputs. Example: ```ruby -field :created_at, Types::TimeType, null: false, description: 'Timestamp of when the issue was created' +field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created' ``` ## Testing diff --git a/doc/user/project/issues/csv_export.md b/doc/user/project/issues/csv_export.md index 8de0f038881..c5b048c1d69 100644 --- a/doc/user/project/issues/csv_export.md +++ b/doc/user/project/issues/csv_export.md @@ -30,7 +30,7 @@ Among numerous use cases for exporting issues for CSV, we can name a few: After selecting a project, from the issues page you can narrow down which issues to export using the search bar, along with the All/Open/Closed tabs. All issues returned will be exported, including those not shown on the first page. -![CSV export button](img/csv_export_button.png) +![CSV export button](img/csv_export_button_v12_9.png) You will be asked to confirm the number of issues and email address for the export, after which the email will begin being prepared. diff --git a/doc/user/project/issues/img/csv_export_button.png b/doc/user/project/issues/img/csv_export_button.png deleted file mode 100644 index f9fcfd71c2fd9a7bffca2938aad0582d7f329cc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7383 zcmaKR1yEeUvi4%ZeIW!0PFM)CcyJGklf_*UEVz^45ZpbuyCk@4aCd?PcXwUbNAA7< zz4xzI^``2a>FMsTdwP1Nr|O&#WkqRh3{ngL0Dvt6k%R&ONWf>>1r6!>Z5l#v``jU$ zi7AKy0F_agFe8-bF@hshS{zU@O1}3@XIQ9eI%z7%^MACpW;Oa`Yiz>mW^MPZ1po-T z@jsK+CQe3FZq`;dj{I&yH2=`xf2RMk*=VT#q2gpIM5C#oOeJCKU_!;s%Ebz%5yqgR zq7roYWXcbfeE%=_^GJxs+{wv~pN-Ac)s@wilhxM2jE$X-kB<$^!N$SC@~pw)_{GM_ z$c@Fuk@lZP{+}I56UUDZ7Isb+wl-9M?HU=|Iy(u`(EN4u@Al7mnz&i~$CHiYzruPB zknOL8jhz+D_HWzIP{F@keq{?c6Dv(g3u_Y_$7dhH?A*LOg8u;gFVTNo{ufm1KTrx+q=EJy}G)(ySsaOdOAHly}!TbYO~(m-F@}y)y>V#;o;%&@i7Sr z$-~3L#l;0FDJd~A@gofO^5sj;R;#0%*+{rpqy~*$2zu#6E!{M;2%ggokH4b);Pwl~qiVBpJluvMYW>$7tS=r>o zM0;DCnuKb|8aAs_tiU0GS7rKR)r^$quThd(~RACA+4znYqwX4j0kyL)^Z z_+n^ixV5!K#Rqk?b7Em;fnBZ-4i4CV34Gh)6c7|#UzB{fHhcOASL?Qs>-$zvP#Bt! z$M?%OF0JO|`XSiev2*sI=4e+^-cG~R%_Sh7mjX1@RBY+sWiBr=wst{Ih?!@?=04dLyoDkx}~={&!@zdq~RsRng;ZXI8-OK5f$ z#kP)b6lTPyf6?omS-*aQZ!AtPA3SVd!6QO~zIlHws&0<4llh=!+}Ja@ynE{IA9l98 zRFDuDQ{E@kW*N3NWMmV)cK`5R)A=o<(8cLtz()xwAzr_+kh6QZ(`dX&VA=TmMrnw# zmz#5HOh|NULEi5CP~~?)Dq^jHU;$a9+lQx|wa%0M&Gdzg$h=l}D{Un~mgwkcHN()O zv&-3odntLf`PJRF-R{idTK}vDGIq$;<@WgT?A`8EWs;YjmYn%;WX)Q|&dpI;dB^D0 zmE2&Qt+7VnOq#d4l*e3gkc07DS4B;8pU#(zt=$6!tB7cKGq=D3N;0~+hWwlRo!>`2 z?#}kw6Qxek_2v^f9!s61WK8rFT(=MBy(gO{bB!;ExwKpS)MC#pGALz=D`e z_82sip$Oa{QBqst>gw1Ge%Z;@xIsj18HM8*H_cJCdH z$zZy(e{RQnGU!@WL{dFUu(rD>^7wh=YN`X+WCDpKp8A-6v2XkiJt-NlNYE`2n5$jT zW$ESGPmF4QLl%rdhdMMgFl5{eHr*Rnp*Pf4ABLI@9Pu_Fl+|hgcs~LDaMAS*P7OFT zA2>9Z4q_0>XDmW@R{t;ufv7gEou;DkfM^(W$S8h@=wiNEqTG5pW?B*Cq<>@_!XWQ76%<@!}`~ZxJ!T=9^sj z4nE7x7HxVd8T%7KmK{EZn;;K8Me4@ZJ2pARZs8v`L6x8Sge4%vS#BrgbGd%TcXi^O z!atY2+sFBVOO7T=5B9_b2iz?lVyndQESrXr-^95J9GY!BlUJ)Xg7-fA?+uq{TN$so zOgV^*unr8hoLV^`c6+Sv_ipQyYuV){q6^#RQ`cy|Lkzw33}`bV!|7hc=;GH%PX8TF@->tC}WHvs$Zac$X9w_qa>Z%{M0u>P^X)H8BdSO-_=C z;CQ3bVHPWmc(5VQNteL%!waJ$T_sU~cmM`sgLyl2)fkE0yT!qi>~2QaP6&b{Y9#bV zwoj9>7O|;Y#T7f>y{Zvr9OnVKAp@}%vj8t>6-9kVuu+w;SDOaBTC&V~gI7|gjk$nL z6RX8vXbZdv1iAJ7*}7gF4#;Cy1W`tjHC~Ixl=hD~T9TF2hzAIaNa|QUlp9QK4EKcY z9Z#N%7Vbl}(BB!Ogx(utU{FUnOv0>hi6+sbPK>X}Jl-UEqp*4JTt1+8zh7qDFWh>0 zggw|ULni)f9-psAT1RZ5^Qz5eHhWd__Qh)@B_-K=bt@!beP+nE1QqaB_Xp@!(y3i| zZ7s2pP>T=6#QqKFvO1?&AMB6oziN1DC#U3uqdy*9I>YO;c>2*j3vWh49luiA#oqGs zvVY4api`G>MRn)qrUC0 zmJue63p*Vqx+@QOxxWgf41ecP24qf?u8~5gkme#0e;=Te?Cg_OB|;-Ld}x}|Q@T%f zyeTx8Sg_cR_P>1t{aU;z6?K+xy>2Lm7&rV46;E^mn%V!k;Pg`|)W)>jRHd31fa%)|$4clg>hsSMw z`L?oMQMfo>iE)$nnOnpHd6!~>im+D!<3(kSuLLnzkwA>U^E4o&l4BN^Sct8`BiI&PpWLym@h(I2Z`n9`!nY7 zA03;bQ3e$*6F*#5&kp^B_)u;Os^a8pOy)L#eXCT>s>~KN7qB}@^v}I!W2^kp_b<|8{ zV9+n#Nn((uTT~Rn9PY2W#Yd8-r5$Q@^)SL6 zr9O6d*{VPJdMx4NatS*|bZTE>G%_8C$rT#G6SH1JYrFiTQHsPwJd8vpn3*3%Sm*690Uu zBFHs`+BvYMR1q;7O#_twvI1%1u@lS0o&h|>*s{A<_XAQMKZ%K=WIp1QDxN}}@Q2P3 z18*Xm{9E7h)weSxv5(bU66opNJf)nWN>F9D$&Y*}{MoIiODxUiG?^cK#x@NkK#Jqa z$}YdVD7O}QTAo}>)O+8<+k2@)uG`HMBz1@y%G}5d(C8>XR)fj=(D;PkH zWBmCG{qZt#fT8>S*s&LaGR(i#G0J+MAL%w^Bd9^9BHfbrI+5{6p}GG$q^uiaV>}l5 zS|o(fZ-XDKCL=3YnIN7k)Eu!y6_HC>d$)-?8;qA*MyS?=PVi!3A%w$% zKpZ&T_f)KUtyFrXD-iOUSOBLIq534LueDA1X79vx?9LbY{9l#?Qpq%dhKiv=rxC$^*V4=Jn&`EF|+p zC_|%Lc^~g$e`|ju$eA1z`u0<~9uMoT|^75eOg#DBNFu!bnL$N>aQ)@!=iP)A9jSSN<#f(!bk}`+^ zAvPaXiZmpptR`CTl@^~a?1K%$`66jTIwKc zopFWM%ugh$6~*<0dI%tKqeLG-cRdf5Z0d9QAy47fiEq3$82qhgm23UgevdQ-OR1XU z#=VbqY-;?{5fU*Q9b3x(GttbsQHxW_5;u7k8P;2oRv=o=Xl*KD44Eahi)+ZB$+M)16>ldM& zZ8S*g`{^9fi2crtDfxnJZVT4Tm|KfW3dW24+}?)TR=R~-$*n~}CP>tosa%;P&f^xR z4?vny-*9}Hg_xKvQZ>zELh6jM4wsxPKamdc9Be^s?tqc(=tdcZjw*$0fXluoz9#?` zCR{$)$*~q4HAHP^re+<}FOI891!tl`UkoId1oTQaYDtYpGBYRt(97?oLOFHX1Ty0s zbbk$KF77(uL8Db52oTWy6e9Cs-!aZX(X;wj(anT|Z6VPsc30=ouBv06sIy!38~&sU z%xVRCuPDq46@Zd2u!-g9NNho4{7q#QQvZU5bUF^lX?9jp9yW)uL}A$Wi%5%46Wgh^ z#1(6jDlVVut~MXADb%8@wGBIegQSj_QJ{cE)`05L%DlWKo1p{ydftT>gU*7>K610P zn{yO>%TO}8wG9)mSj~+(@wT?XLykH1c*>o9q~5(aSv5Uv^&|YEJar^mPOU5b=PlXD ziXtV|j{f`8JLJ@$!ofO49`imX54l*<1pu(mAf8@^D_Vd24d7%$cL~-zXR9lZGlQ@3XI*g#Ws1E zfB*`@L?M7syUJ}Z6roU+Z(2BBQ&ygP-2kSqw)<4NE7|}04AWWGa6)ygn?_8u)Z+w6qgf0w4M2>4JF{uBpgg(IM@&xq(0wDP5X4?-sJp=ix8pBY4>rd1~ zR+Jx4Y{|Mqzke`QGX;D%03L8merHo)lB#w{tN_+ZA6x?u{`}Ten?V!1;L0A1$^c_d zMjJmKq`uS^?<>Ha)Eq0-xfTX*^;eY1Qjgwfmh0BUc~jd@rsU`sm%R}g+z;5$KJ7ie zKud2;@u+nt*RIw6bMs*m(ZDJQG{nx}Br7C-m%+LgoAaE9B%?@230`C^pI~)pF`ehDRyd!6|ttJ(xlgy z%qFc!?RKkOrhR^*p!wjGSHKOMTr}TUEst?ERvEyV4&Htpv8X{k><~snfOoGQ1=Y%I z1CF(>gELxmg4Aunq|GZfjWl!DdSSxsZbRmaPmH7o%UR3ho~K@ao$0CWvPo)q2thy}cq1r@5!kQ62Uv z4+UKI(qNk=PR7P@6E1fu724noSRqFF3cuk2g-DYvId&j#ZtkF&+)7jRfk&S^QvI7q zZt}WU?l;-no(Bop%Qpk_ zz?pK&l<*eQdmOzy22^2f5iX;~jU_=9w-Z~Qe%F<9@+l{sd+F;Ob&bre0!tcJDY4cW zl@GG8VO<64lBku|6-rrF6oQyPnTtb#)k}(3kt?SJu5f45lhuS6kz)ylwxe3zyC=Xs z$GBzV#iI!qY#V|;@kmAD++ zwTK}$T&*_q)}#TuSJ-Vt6c4ri)s^%_>lx;UbwNS?=?6jc9rziVr^JT<8yt7sF+0?yhE$87OLj zv5lzh57ywKjBq(Iz#E|^W%)aG^boWLk{Ov4uhLy1}3Zcc(^!BmQ6W7t0p}i@zd1uBz zeG!Ve{OQ#_QD3>pmynWk3E z z4ESPutMSpZ$n5mP?X~mSOd;Is>rd)T%TSBN92OFLtW53vSLKdLZl{faoUvGZt zzDjq@EuPR}J*myF+c9gF8Ga=gHH$JDa7$=#Qrs$_e6GYbvTx}6IKARpUFzXQd0)&0$V=``Y&rpg&(0@_xpCp)gh)2 zJ~|*O4Mg(E$|iaQlR{!Jp#24&q_3*d`g1o33J@VN9MDxbBOvejzJrQl02rxIe1Or2 zq8JbamuBF0&0!u^c7M7k5IEgMII_sa2!8U4v!Rx(Ibtjyai2=Q9q zzu`Ab5xTpxEm7ijyL&h0^+kn~i6Qzl=OO|a0ng^2tv_F^(TF~JbUn6PFS0;koCI=z Pe>uuXDN0s|8~XhZ*D?XT diff --git a/doc/user/project/issues/img/csv_export_button_v12_9.png b/doc/user/project/issues/img/csv_export_button_v12_9.png new file mode 100644 index 0000000000000000000000000000000000000000..88083cacbfbb9db43a519913d11ab42abfca44f4 GIT binary patch literal 34524 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 literal 0 HcmV?d00001 diff --git a/lib/gitlab/email/attachment_uploader.rb b/lib/gitlab/email/attachment_uploader.rb index d8962ec0d20..70e281fd4a3 100644 --- a/lib/gitlab/email/attachment_uploader.rb +++ b/lib/gitlab/email/attachment_uploader.rb @@ -39,15 +39,22 @@ module Gitlab # from the uploaded attachments def filter_signature_attachments(message) attachments = message.attachments + content_type = normalize_mime(message.content_type) + protocol = normalize_mime(message.content_type_parameters[:protocol]) - if message.content_type&.starts_with?('multipart/signed') - signature_protocol = message.content_type_parameters[:protocol] - - attachments.delete_if { |attachment| attachment.content_type.starts_with?(signature_protocol) } if signature_protocol.present? + if content_type == 'multipart/signed' && protocol + attachments.delete_if { |attachment| protocol == normalize_mime(attachment.content_type) } end attachments end + + # normalizes mime-type ignoring case and removing extra data + # also removes potential "x-" prefix from subtype, since some MUAs mix them + # e.g. "application/x-pkcs7-signature" with "application/pkcs7-signature" + def normalize_mime(content_type) + MIME::Type.simplified(content_type, remove_x_prefix: true) + end end end end diff --git a/spec/fixtures/emails/valid_reply_signed_smime_mixed_protocol_prefix.eml b/spec/fixtures/emails/valid_reply_signed_smime_mixed_protocol_prefix.eml new file mode 100644 index 00000000000..551b2f217e9 --- /dev/null +++ b/spec/fixtures/emails/valid_reply_signed_smime_mixed_protocol_prefix.eml @@ -0,0 +1,294 @@ +User-Agent: Microsoft-MacOutlook/10.22.0.200209 +Date: Mon, 17 Feb 2020 22:56:47 +0100 +Subject: Re: htmltest | test issue (#1) +From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)" + +To: Administrator / htmltest + +Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com> +Thread-Topic: htmltest | test issue (#1) +References: + + +In-Reply-To: +Content-type: multipart/signed; + protocol="application/x-pkcs7-signature"; + micalg=sha256; + boundary="B_3664825007_1904734766" +MIME-Version: 1.0 + +--B_3664825007_1904734766 +Content-type: multipart/mixed; + boundary="B_3664825007_384940722" + + +--B_3664825007_384940722 +Content-type: multipart/alternative; + boundary="B_3664825007_1519466360" + + +--B_3664825007_1519466360 +Content-type: text/plain; + charset="UTF-8" +Content-transfer-encoding: quoted-printable + +Me too, with an attachment + +=20 + +From: Administrator +Reply to: Administrator / htmltest +Date: Monday, 17 February 2020 at 22:55 +To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" +Subject: Re: htmltest | test issue (#1) + +=20 + +Administrator commented:=20 + +I pity the foo !!! + +=E2=80=94=20 +Reply to this email directly or view it on GitLab.=20 +You're receiving this email because of your account on 169.254.169.254. If = +you'd like to receive fewer emails, you can unsubscribe from this thread or = +adjust your notification settings.=20 + + +--B_3664825007_1519466360 +Content-type: text/html; + charset="UTF-8" +Content-transfer-encoding: quoted-printable + +GitLab

Me too, with an attachment

 

From: Administrator <dlouzan.dummy@gma= +il.com>
Reply to: Administrator / htmltest <dlouzan.dummy+c0= +34670b1623e617e15a3df64223d363@gmail.com>
Date: Monday, 17 Febr= +uary 2020 at 22:55
To: "Louzan Martinez, Diego (ext) (SOP IT = +STG XS)" <diego.louzan.ext@siemens.com>
Subject: Re: ht= +mltest | test issue (#1)

= + 

Administrator commented:

<= +div>

I pity the foo !!!

=E2=80=94
Reply to this = +email directly or view it on GitLab.
You're receiving this email because of you= +r account on 169.254.169.254. If you'd like to receive fewer emails, you can= + unsubscribe from this thread or adjust your notific= +ation settings.

+ +--B_3664825007_1519466360-- + + +--B_3664825007_384940722 +Content-type: image/png; name="gitlab_logo.png"; + x-mac-creator="4F50494D"; + x-mac-type="504E4766" +Content-disposition: attachment; + filename="gitlab_logo.png" +Content-transfer-encoding: base64 + + +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 +--B_3664825007_384940722-- + +--B_3664825007_1904734766 +Content-type: application/pkcs7-signature; name="smime.p7s" +Content-transfer-encoding: base64 +Content-disposition: attachment; + filename="smime.p7s" + +MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B +BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC +REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l +bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/ +MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh +dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI +WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW +BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt +JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK +z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg +v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU +ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM +faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy +BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI +KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl +cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa +QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz +cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii +tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr +BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg +gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v +Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/ +Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDgYDVR0PAQH/BAQDAgeAMFUGA1UdEQROMEygLAYKKwYBBAGCNxQCA6AeDBxkaWVnby5s +b3V6YW4uZXh0QHNpZW1lbnMuY29tgRxkaWVnby5sb3V6YW4uZXh0QHNpZW1lbnMuY29tMB0G +A1UdDgQWBBQj8k8aqZey68w8ALYKGJSGMt5hZDANBgkqhkiG9w0BAQsFAAOCAgEAFDHqxpb1 +R9cB4noC9vx09bkNbmXCpVfl3XCQUmAWTznC0nwEssTTjo0PWuIV4C3jnsp0MRUeHZ6lsyhZ +OzS1ETwYgvj6wzjb8RF3wgn7N/JOvFGaErMz5HZpKOfzGiNpW6/Rmd4hsRDjAwOVQOXUTqc/ +0Bj3FMoLRCSWSnTp5HdyvrY2xOKHfTrTjzmcLdFaKE2F5n7+dBkwCKVfzut8CqfVq/I7ks4m +D1IHk93/P6l9U34R2FHPt6zRTNZcWmDirRSlMH4L18CnfiNPuDN/PtRYlt3Vng5EdYN0VCg2 +NM/uees0U4ingCb0NFjg66uQ/tjfPQk55MN4Wpls4N6TkMoTCWLiqZzYTGdmVQexzroL6940 +tmMr8LoN3TpPf0OdvdKEpyH7fzsx5QlmQyywIWec6X+Fx6+l0g91VJnPEtqACpfZIBZtviHl +gfX298w+SsvBK8C48Pqs8Ijh7tLrCxx7VMLVHZqwWWPK53ga+CDWmjoSQPxi+CPZF7kao6N5 +4GrJWwSHlHh6WzTbLyLvTJZZ775Utp4W8s8xMUsQJ413iYzEaC8FcSeNjSk5UiDDiHrKmzpM +tbApD3pUXStblUMKYGTG1Mj9BcEBFkCdoGlw/ulszIrKFfOyRNDG3Ay+Dj/oMjoKsJphu3px +wyft82rTer7UW/I7o0h0DAG4lkMwggdrMIIFU6ADAgECAgR5nlqfMA0GCSqGSIb3DQEBCwUA +MIGeMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQ +MA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTMxHTAbBgNVBAsMFFNpZW1lbnMg +VHJ1c3QgQ2VudGVyMScwJQYDVQQDDB5TaWVtZW5zIElzc3VpbmcgQ0EgRUUgRW5jIDIwMTYw +HhcNMTkwOTI3MDgwMTM5WhcNMjAwOTI3MDgwMTM3WjB3MREwDwYDVQQFEwhaMDAzSDA4VDEO +MAwGA1UEKgwFRGllZ28xGDAWBgNVBAQMD0xvdXphbiBNYXJ0aW5lejEYMBYGA1UECgwPU2ll +bWVucy1QYXJ0bmVyMR4wHAYDVQQDDBVMb3V6YW4gTWFydGluZXogRGllZ28wggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyby5qKzZIrGYWRqxnaAyMt/a/uc0uMk0F3MjwxvPM +vh5DllUpqx0l8ZDakDjPhlEXTeoL4DHNgmh+CDCs76CppM3cNG/1W1Ajo/L2iwMoXaxYuQ/F +q7ED+02KEkWX2DDVVG3fhrUGP20QAq77xPDptmVWZnUnuobZBNYkC49Xfl9HJvkJL8P0+Jqb +Eae7p4roiEr7wNkGriwrVXgA3oPNF/W+OuI76JTNTajS/6PAK/GeqIvLjfuBXpdBZTY031nE +Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg +lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw +JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC +BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV +HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu +Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh +dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89 +VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG +AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv +MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr +BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a +WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa +WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu +cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF +BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL +BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf +eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8 +R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs +hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6 +7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD +DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3 +ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv +K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ +lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut +7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ +BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK +DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD +ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0 +aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi +BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI +hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB +AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW +DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR +sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx +8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI +YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs= + +--B_3664825007_1904734766-- diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb index a3015a34afe..daed5725e26 100644 --- a/spec/graphql/types/base_field_spec.rb +++ b/spec/graphql/types/base_field_spec.rb @@ -203,7 +203,7 @@ describe Types::BaseField do end it 'raises an error if a required property is missing', :aggregate_failures do - expect { test_field(deprecated: { milestone: 1.0 }) }.to raise_error( + expect { test_field(deprecated: { milestone: '1.10' }) }.to raise_error( ArgumentError, 'Please provide a `reason` within `deprecated`' ) @@ -212,37 +212,44 @@ describe Types::BaseField do 'Please provide a `milestone` within `deprecated`' ) end + + it 'raises an error if milestone is not a String', :aggregate_failures do + expect { test_field(deprecated: { milestone: 1.10, reason: 'Deprecation reason' }) }.to raise_error( + ArgumentError, + '`milestone` must be a `String`' + ) + end end it 'adds a formatted `deprecated_reason` to the field' do - field = test_field(deprecated: { milestone: 1.0, reason: 'Deprecation reason' }) + field = test_field(deprecated: { milestone: '1.10', reason: 'Deprecation reason' }) - expect(field.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.0') + expect(field.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10') end it 'appends to the description if given' do field = test_field( - deprecated: { milestone: 1.0, reason: 'Deprecation reason' }, + deprecated: { milestone: '1.10', reason: 'Deprecation reason' }, description: 'Field description' ) - expect(field.description).to eq('Field description. Deprecated in 1.0: Deprecation reason') + expect(field.description).to eq('Field description. Deprecated in 1.10: Deprecation reason') end it 'does not append to the description if it is absent' do - field = test_field(deprecated: { milestone: 1.0, reason: 'Deprecation reason' }) + field = test_field(deprecated: { milestone: '1.10', reason: 'Deprecation reason' }) expect(field.description).to be_nil end it 'interacts well with the `feature_flag` property' do field = test_field( - deprecated: { milestone: 1.0, reason: 'Deprecation reason' }, + deprecated: { milestone: '1.10', reason: 'Deprecation reason' }, description: 'Field description', feature_flag: 'foo_flag' ) - expect(field.description).to eq('Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.0: Deprecation reason') + expect(field.description).to eq('Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason') end end end diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index 462be76a58d..5104e0a3f8f 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -31,5 +31,20 @@ describe Gitlab::Email::AttachmentUploader do expect(image_link[:url]).to include('gitlab_logo.png') end end + + context 'with a signed message with mixed protocol prefix' do + let(:message_raw) { fixture_file("emails/valid_reply_signed_smime_mixed_protocol_prefix.eml") } + + it 'uploads all attachments except the signature' do + links = described_class.new(message).execute(upload_parent: project, uploader_class: FileUploader) + + expect(links).not_to include(a_hash_including(alt: 'smime.p7s')) + + image_link = links.first + expect(image_link).not_to be_nil + expect(image_link[:alt]).to eq('gitlab_logo') + expect(image_link[:url]).to include('gitlab_logo.png') + end + end end end diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb index 3982c79e23c..87079a1786c 100644 --- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb +++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb @@ -76,7 +76,7 @@ describe Gitlab::Graphql::Docs::Renderer do Class.new(Types::BaseObject) do graphql_name 'DeprecatedTest' - field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: 1.0 }, description: 'A description' + field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description' end end @@ -86,7 +86,7 @@ describe Gitlab::Graphql::Docs::Renderer do | Name | Type | Description | | --- | ---- | ---------- | - | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.0 | + | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10 | DOC is_expected.to include(expectation)