Commit 4c61a186 authored by Amy Qualls's avatar Amy Qualls

Merge branch 'caw-spam-captcha-rest-api-docs' into 'master'

Adds spam/CAPTCHA dev docs for REST API

See merge request gitlab-org/gitlab!83221
parents 523d369a c0054883
...@@ -594,6 +594,7 @@ Sobelow ...@@ -594,6 +594,7 @@ Sobelow
Solargraph Solargraph
Solarized Solarized
Sourcegraph Sourcegraph
spammable
sparkline sparkline
sparklines sparklines
spidering spidering
......
...@@ -13,28 +13,27 @@ related to changing a model's confidential/public flag. ...@@ -13,28 +13,27 @@ related to changing a model's confidential/public flag.
## Add support to the GraphQL mutations ## Add support to the GraphQL mutations
This implementation is very similar to the controller implementation. You create a `spam_params` The main steps are:
instance based on the request, and pass it to the relevant Service class constructor.
The three main differences from the controller implementation are: 1. Use `include Mutations::SpamProtection` in your mutation.
1. Create a `spam_params` instance based on the request. Obtain the request from the context
via `context[:request]` when creating the `SpamParams` instance.
1. Pass `spam_params` to the relevant Service class constructor.
1. After you create or update the `Spammable` model instance, call `#check_spam_action_response!`
and pass it the model instance. This call:
1. Performs the necessary spam checks on the model.
1. If spam is detected:
- Raises a `GraphQL::ExecutionError` exception.
- Includes the relevant information added as error fields to the response via the `extensions:` parameter.
For more details on these fields, refer to the section in the GraphQL API documentation on
[Resolve mutations detected as spam](../../api/graphql/index.md#resolve-mutations-detected-as-spam).
1. Use `include Mutations::SpamProtection` instead of `...JsonFormatActionsSupport`. NOTE:
1. Obtain the request from the context via `context[:request]` when creating the `SpamParams` If you use the standard ApolloLink or Axios interceptor CAPTCHA support described
instance. above, you can ignore the field details, because they are handled
1. After you create or updated the `Spammable` model instance, call `#check_spam_action_response!` automatically. They become relevant if you attempt to use the GraphQL API directly to
and pass it the model instance. This call will: process a failed check for potential spam, and resubmit the request with a solved
1. Perform the necessary spam checks on the model. CAPTCHA response.
1. If spam is detected:
- Raise a `GraphQL::ExecutionError` exception.
- Include the relevant information added as error fields to the response via the `extensions:` parameter.
For more details on these fields, refer to the section on
[Spam and CAPTCHA support in the GraphQL API](../../api/graphql/index.md#resolve-mutations-detected-as-spam).
NOTE:
If you use the standard ApolloLink or Axios interceptor CAPTCHA support described
above, the field details are unimportant. They become important if you
attempt to use the GraphQL API directly to process a failed check for potential spam, and
resubmit the request with a solved CAPTCHA response.
For example: For example:
...@@ -57,10 +56,13 @@ module Mutations ...@@ -57,10 +56,13 @@ module Mutations
widget = service_response.payload[:widget] widget = service_response.payload[:widget]
check_spam_action_response!(widget) check_spam_action_response!(widget)
# If possible spam wasdetected, an exception would have been thrown by # If possible spam was detected, an exception would have been thrown by
# `#check_spam_action_response!`, so the normal resolve return logic can follow below. # `#check_spam_action_response!`, so the normal resolve return logic can follow below.
end end
end end
end end
end end
``` ```
Refer to the [Exploratory Testing](exploratory_testing.md) section for instructions on how to test
CAPTCHA behavior in the GraphQL API.
...@@ -16,7 +16,7 @@ To add this support, you must implement the following areas as applicable: ...@@ -16,7 +16,7 @@ To add this support, you must implement the following areas as applicable:
1. [Model and Services](model_and_services.md): The basic prerequisite 1. [Model and Services](model_and_services.md): The basic prerequisite
changes to the backend code which are required to add spam or CAPTCHA API and UI support changes to the backend code which are required to add spam or CAPTCHA API and UI support
for a feature which does not yet have support. for a feature which does not yet have support.
1. REST API (Supported, documentation coming soon): The changes needed to add 1. [REST API](rest_api.md): The changes needed to add
spam or CAPTCHA support to Grape REST API endpoints. Refer to the related spam or CAPTCHA support to Grape REST API endpoints. Refer to the related
[REST API documentation](../../api/index.md#resolve-requests-detected-as-spam). [REST API documentation](../../api/index.md#resolve-requests-detected-as-spam).
1. [GraphQL API](graphql_api.md): The changes needed to add spam or CAPTCHA support to GraphQL 1. [GraphQL API](graphql_api.md): The changes needed to add spam or CAPTCHA support to GraphQL
......
---
stage: Manage
group: Authentication and Authorization
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# REST API spam protection and CAPTCHA support
If the model can be modified via the REST API, you must also add support to all of the
relevant API endpoints which may modify spammable or spam-related attributes. This
definitely includes the `POST` and `PUT` mutations, but may also include others, such as those
related to changing a model's confidential/public flag.
## Add support to the REST endpoints
The main steps are:
1. Add `helpers SpammableActions::CaptchaCheck::RestApiActionsSupport` in your `resource`.
1. Create a `spam_params` instance based on the request.
1. Pass `spam_params` to the relevant Service class constructor.
1. After you create or update the `Spammable` model instance, call `#check_spam_action_response!`,
save the created or updated instance in a variable.
1. Identify the error handling logic for the `failure` case of the request,
when create or update was not successful. These indicate possible spam detection,
which adds an error to the `Spammable` instance.
The error is usually similar to `render_api_error!` or `render_validation_error!`.
1. Wrap the existing error handling logic in a
`with_captcha_check_rest_api(spammable: my_spammable_instance)` call, passing the `Spammable`
model instance you saved in a variable as the `spammable:` named argument. This call will:
1. Perform the necessary spam checks on the model.
1. If spam is detected:
- Raise a Grape `#error!` exception with a descriptive spam-specific error message.
- Include the relevant information added as error fields to the response.
For more details on these fields, refer to the section in the REST API documentation on
[Resolve requests detected as spam](../../api/index.md#resolve-requests-detected-as-spam).
NOTE:
If you use the standard ApolloLink or Axios interceptor CAPTCHA support described
above, you can ignore the field details, because they are handled
automatically. They become relevant if you attempt to use the GraphQL API directly to
process a failed check for potential spam, and resubmit the request with a solved
CAPTCHA response.
Here is an example for the `post` and `put` actions on the `snippets` resource:
```ruby
module API
class Snippets < ::API::Base
#...
resource :snippets do
# This helper provides `#with_captcha_check_rest_api`
helpers SpammableActions::CaptchaCheck::RestApiActionsSupport
post do
#...
spam_params = ::Spam::SpamParams.new_from_request(request: request)
service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute
snippet = service_response.payload[:snippet]
if service_response.success?
present snippet, with: Entities::PersonalSnippet, current_user: current_user
else
# Wrap the normal error response in a `with_captcha_check_rest_api(spammable: snippet)` block
with_captcha_check_rest_api(spammable: snippet) do
# If possible spam was detected, an exception would have been thrown by
# `#with_captcha_check_rest_api` for Grape to handle via `error!`
render_api_error!({ error: service_response.message }, service_response.http_status)
end
end
end
put ':id' do
#...
spam_params = ::Spam::SpamParams.new_from_request(request: request)
service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute(snippet)
snippet = service_response.payload[:snippet]
if service_response.success?
present snippet, with: Entities::PersonalSnippet, current_user: current_user
else
# Wrap the normal error response in a `with_captcha_check_rest_api(spammable: snippet)` block
with_captcha_check_rest_api(spammable: snippet) do
# If possible spam was detected, an exception would have been thrown by
# `#with_captcha_check_rest_api` for Grape to handle via `error!`
render_api_error!({ error: service_response.message }, service_response.http_status)
end
end
end
```
...@@ -37,7 +37,7 @@ additional fields being added to the models. Instead, communication is handled: ...@@ -37,7 +37,7 @@ additional fields being added to the models. Instead, communication is handled:
The spam and CAPTCHA-related logic is also cleanly abstracted into reusable modules and helper methods The spam and CAPTCHA-related logic is also cleanly abstracted into reusable modules and helper methods
which can wrap existing logic, and only alter the existing flow if potential spam which can wrap existing logic, and only alter the existing flow if potential spam
is detected or a CAPTCHA display is needed. This approach allows the spam and CAPTCHA is detected or a CAPTCHA display is needed. This approach allows the spam and CAPTCHA
support to be easily added to new areas of the application with minimal changes to support to be added to new areas of the application with minimal changes to
existing logic. In the case of the frontend, potentially **zero** changes are needed! existing logic. In the case of the frontend, potentially **zero** changes are needed!
On the frontend, this is handled abstractly and transparently using `ApolloLink` for Apollo, and an On the frontend, this is handled abstractly and transparently using `ApolloLink` for Apollo, and an
...@@ -75,7 +75,7 @@ sequenceDiagram ...@@ -75,7 +75,7 @@ sequenceDiagram
The backend is also cleanly abstracted via mixin modules and helper methods. The three main The backend is also cleanly abstracted via mixin modules and helper methods. The three main
changes required to the relevant backend controller actions (normally just `create`/`update`) are: changes required to the relevant backend controller actions (normally just `create`/`update`) are:
1. Create a `SpamParams` parameter object instance based on the request, using the simple static 1. Create a `SpamParams` parameter object instance based on the request, using the static
`#new_from_request` factory method. This method takes a request, and returns a `SpamParams` instance. `#new_from_request` factory method. This method takes a request, and returns a `SpamParams` instance.
1. Pass the created `SpamParams` instance as the `spam_params` named argument to the 1. Pass the created `SpamParams` instance as the `spam_params` named argument to the
Service class constructor, which you should have already added. If the spam check indicates Service class constructor, which you should have already added. If the spam check indicates
......
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