mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 21:08:07 +01:00
Document ProvisioningController and ProvisioningConnectListener
This commit is contained in:
committed by
Jon Chambers
parent
7a6ce00fed
commit
0a1161048f
@@ -57,6 +57,7 @@ import org.whispersystems.textsecuregcm.entities.DeviceInfoList;
|
||||
import org.whispersystems.textsecuregcm.entities.DeviceResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.LinkDeviceRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKeySignatureValidator;
|
||||
import org.whispersystems.textsecuregcm.entities.ProvisioningMessage;
|
||||
import org.whispersystems.textsecuregcm.entities.SetPublicKeyRequest;
|
||||
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
@@ -146,9 +147,34 @@ public class DeviceController {
|
||||
accounts.removeDevice(auth.getAccount(), deviceId).join();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signed device-linking token. Generally, primary devices will include the signed device-linking token in
|
||||
* a provisioning message to a new device, and then the new device will include the token in its request to
|
||||
* {@link #linkDevice(BasicAuthorizationHeader, String, LinkDeviceRequest, ContainerRequest)}.
|
||||
*
|
||||
* @param auth the authenticated account/device
|
||||
*
|
||||
* @return a signed device-linking token
|
||||
*
|
||||
* @throws RateLimitExceededException if the caller has made too many calls to this method in a set amount of time
|
||||
* @throws DeviceLimitExceededException if the authenticated account has already reached the maximum number of linked
|
||||
* devices
|
||||
*
|
||||
* @see ProvisioningController#sendProvisioningMessage(AuthenticatedDevice, String, ProvisioningMessage, String)
|
||||
*/
|
||||
@GET
|
||||
@Path("/provisioning/code")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(
|
||||
summary = "Generate a signed device-linking token",
|
||||
description = """
|
||||
Generate a signed device-linking token for transmission to a pending linked device via a provisioning message.
|
||||
""")
|
||||
@ApiResponse(responseCode="200", description="Token was generated successfully", useReturnTypeSchema=true)
|
||||
@ApiResponse(responseCode = "411", description = "The authenticated account already has the maximum allowed number of linked devices")
|
||||
@ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header(
|
||||
name = "Retry-After",
|
||||
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed"))
|
||||
public VerificationCode createDeviceToken(@ReadOnly @Auth AuthenticatedDevice auth)
|
||||
throws RateLimitExceededException, DeviceLimitExceededException {
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ import io.dropwizard.auth.Auth;
|
||||
import io.dropwizard.util.DataSize;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.Base64;
|
||||
import javax.validation.Valid;
|
||||
@@ -34,6 +37,16 @@ import org.whispersystems.textsecuregcm.push.ProvisioningManager;
|
||||
import org.whispersystems.textsecuregcm.websocket.ProvisioningAddress;
|
||||
import org.whispersystems.websocket.auth.ReadOnly;
|
||||
|
||||
/**
|
||||
* The provisioning controller facilitates transmission of provisioning messages from the primary device associated with
|
||||
* an existing Signal account to a new device. To send a provisioning message, a primary device generally scans a QR
|
||||
* code displayed by the new device that contains the device's "provisioning address" and a public key. The primary
|
||||
* device then encrypts a use-case-specific provisioning message and posts it to
|
||||
* {@link #sendProvisioningMessage(AuthenticatedDevice, String, ProvisioningMessage, String)}, at which point the server
|
||||
* delivers the message to the new device via an open provisioning WebSocket.
|
||||
*
|
||||
* @see org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener
|
||||
*/
|
||||
@Path("/v1/provisioning")
|
||||
@Tag(name = "Provisioning")
|
||||
public class ProvisioningController {
|
||||
@@ -56,21 +69,34 @@ public class ProvisioningController {
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public void sendProvisioningMessage(@ReadOnly @Auth AuthenticatedDevice auth,
|
||||
@PathParam("destination") String destinationName,
|
||||
@NotNull @Valid ProvisioningMessage message,
|
||||
@HeaderParam(HttpHeaders.USER_AGENT) String userAgent)
|
||||
@Operation(
|
||||
summary = "Send a provisioning message to a new device",
|
||||
description = """
|
||||
Send a provisioning message from an authenticated device to a device that (presumably) is not yet associated
|
||||
with a Signal account.
|
||||
""")
|
||||
@ApiResponse(responseCode="204", description="The provisioning message was delivered to the given provisioning address")
|
||||
@ApiResponse(responseCode="400", description="The provisioning message was too large")
|
||||
@ApiResponse(responseCode="404", description="No device with the given provisioning address was connected at the time of the request")
|
||||
public void sendProvisioningMessage(@ReadOnly @Auth final AuthenticatedDevice auth,
|
||||
|
||||
@Parameter(description = "The temporary provisioning address to which to send a provisioning message")
|
||||
@PathParam("destination") final String provisioningAddress,
|
||||
|
||||
@Parameter(description = "The provisioning message to send to the given provisioning address")
|
||||
@NotNull @Valid final ProvisioningMessage message,
|
||||
|
||||
@HeaderParam(HttpHeaders.USER_AGENT) final String userAgent)
|
||||
throws RateLimitExceededException {
|
||||
|
||||
if (message.body().length() > MAX_MESSAGE_SIZE) {
|
||||
Metrics.counter(REJECT_OVERSIZE_MESSAGE_COUNTER, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent)))
|
||||
.increment();
|
||||
Metrics.counter(REJECT_OVERSIZE_MESSAGE_COUNTER, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))).increment();
|
||||
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
rateLimiters.getMessagesLimiter().validate(auth.getAccount().getUuid());
|
||||
|
||||
if (!provisioningManager.sendProvisioningMessage(ProvisioningAddress.create(destinationName),
|
||||
if (!provisioningManager.sendProvisioningMessage(ProvisioningAddress.create(provisioningAddress),
|
||||
Base64.getMimeDecoder().decode(message.body()))) {
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user