update openapi docs for several endpoints, notably those with PQXDH changes

Co-authored-by: Katherine Yen <katherine@signal.org>
This commit is contained in:
Jonathan Klabunde Tomer 2023-07-06 18:45:33 -04:00 committed by GitHub
parent 098b177bd3
commit e5f4c17148
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 34 deletions

View File

@ -83,6 +83,7 @@ public class AccountControllerV2 {
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Change number", description = "Changes a phone number for an existing account.")
@ApiResponse(responseCode = "200", description = "The phone number associated with the authenticated account was changed successfully", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Verification failed for the provided Registration Recovery Password")
@ApiResponse(responseCode = "409", description = "Mismatched number of devices or device ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = MismatchedDevices.class)))
@ApiResponse(responseCode = "410", description = "Mismatched registration ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = StaleDevices.class)))
@ -159,7 +160,17 @@ public class AccountControllerV2 {
@Path("/phone_number_identity_key_distribution")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Updates key material for the phone-number identity for all devices and sends a synchronization message to companion devices")
@Operation(summary = "Set phone-number identity keys",
description = "Updates key material for the phone-number identity for all devices and sends a synchronization message to companion devices")
@ApiResponse(responseCode = "200", description = "Indicates the transaction was successful and returns basic information about this account.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "This endpoint can only be invoked from the account's primary device.")
@ApiResponse(responseCode = "422", description = "The request body failed validation.")
@ApiResponse(responseCode = "425", description = "Not all of this account's devices support phone-number identities yet.")
@ApiResponse(responseCode = "409", description = "The set of devices specified in the request does not match the set of devices active on the account.",
content = @Content(schema = @Schema(implementation = MismatchedDevices.class)))
@ApiResponse(responseCode = "410", description = "The registration IDs provided for some devices do not match those stored on the server.",
content = @Content(schema = @Schema(implementation = StaleDevices.class)))
public AccountIdentityResponse distributePhoneNumberIdentityKeys(@Auth final AuthenticatedAccount authenticatedAccount,
@NotNull @Valid final PhoneNumberIdentityKeyDistributionRequest request) {

View File

@ -13,8 +13,10 @@ 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.headers.Header;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.Duration;
import java.time.Instant;
@ -86,7 +88,10 @@ public class KeysController {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Returns the number of available one-time prekeys for this device")
@Operation(summary = "Get prekey count",
description = "Gets the number of one-time prekeys uploaded for this device and still available")
@ApiResponse(responseCode = "200", description = "Body contains the number of available one-time prekeys for the device.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
public PreKeyCount getStatus(@Auth final AuthenticatedAccount auth,
@QueryParam("identity") final Optional<String> identityType) {
@ -101,7 +106,15 @@ public class KeysController {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState
@Operation(summary = "Sets the identity key for the account or phone-number identity and/or prekeys for this device")
@Operation(summary = "Upload new prekeys",
description = """
Upload new prekeys for this device. Can also be used, from the primary device only, to set the account's identity
key, but this is deprecated now that accounts can be created atomically.
""")
@ApiResponse(responseCode = "200", description = "Indicates that new keys were successfully stored.")
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Attempt to change identity key from a non-primary device.")
@ApiResponse(responseCode = "422", description = "Invalid request format.")
public void setKeys(@Auth final DisabledPermittedAuthenticatedAccount disabledPermittedAuth,
@RequestBody @NotNull @Valid final PreKeyState preKeys,
@ -176,8 +189,15 @@ public class KeysController {
@GET
@Path("/{identifier}/{device_id}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Retrieves the public identity key and available device prekeys for a specified account or phone-number identity")
public Response getDeviceKeys(@Auth Optional<AuthenticatedAccount> auth,
@Operation(summary = "Fetch public keys for another user",
description = "Retrieves the public identity key and available device prekeys for a specified account or phone-number identity")
@ApiResponse(responseCode = "200", description = "Indicates at least one prekey was available for at least one requested device.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed and unidentified-access key was not supplied or invalid.")
@ApiResponse(responseCode = "404", description = "Requested identity or device does not exist, is not active, or has no available prekeys.")
@ApiResponse(responseCode = "429", description = "Rate limit exceeded.", headers = @Header(
name = "Retry-After",
description = "If present, a positive integer indicating the number of seconds before a subsequent attempt could succeed"))
public PreKeyResponse getDeviceKeys(@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Parameter(description="the account or phone-number identifier to retrieve keys for")
@ -241,9 +261,9 @@ public class KeysController {
final IdentityKey identityKey = usePhoneNumberIdentity ? target.getPhoneNumberIdentityKey() : target.getIdentityKey();
if (responseItems.isEmpty()) {
return Response.status(404).build();
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return Response.ok().entity(new PreKeyResponse(identityKey, responseItems)).build();
return new PreKeyResponse(identityKey, responseItems);
}
@Timed
@ -251,6 +271,13 @@ public class KeysController {
@Path("/signed")
@Consumes(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState
@Operation(summary = "Upload a new signed prekey",
description = """
Upload a new signed elliptic-curve prekey for this device. Deprecated; use PUT /v2/keys with instead.
""")
@ApiResponse(responseCode = "200", description = "Indicates that new prekey was successfully stored.")
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "422", description = "Invalid request format.")
public void setSignedKey(@Auth final AuthenticatedAccount auth,
@Valid final ECSignedPreKey signedPreKey,
@QueryParam("identity") final Optional<String> identityType) {

View File

@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
@ -27,8 +28,8 @@ public record ChangeNumberRequest(
Must not be combined with `recoveryPassword`.""")
String sessionId,
@Schema(description="""
The recovery password for the new phone number, if using a recovery password to authenticate this request.
@Schema(type="string", description="""
The base64-encoded recovery password for the new phone number, if using a recovery password to authenticate this request.
Must not be combined with `sessionId`.""")
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) byte[] recoveryPassword,
@ -43,10 +44,11 @@ public record ChangeNumberRequest(
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
@NotNull IdentityKey pniIdentityKey,
@Schema(description="""
@ArraySchema(
arraySchema=@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keysManager
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.""")
Exactly one message must be supplied for each enabled device other than the sending (primary) device."""))
@NotNull @Valid List<@NotNull @Valid IncomingMessage> deviceMessages,
@Schema(description="""

View File

@ -7,13 +7,23 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.textsecuregcm.util.ECPublicKeyAdapter;
public record ECPreKey(long keyId,
@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
ECPublicKey publicKey) implements PreKey<ECPublicKey> {
public record ECPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24.
""")
long keyId,
@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's elliptic-curve public key format and then base64-encoded.
""")
ECPublicKey publicKey) implements PreKey<ECPublicKey> {
@Override
public byte[] serializedPublicKey() {

View File

@ -7,21 +7,33 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.ECPublicKeyAdapter;
import java.util.Arrays;
import java.util.Objects;
public record ECSignedPreKey(long keyId,
public record ECSignedPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24.
""")
long keyId,
@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
ECPublicKey publicKey,
@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's elliptic-curve public key format and then base64-encoded.
""")
ECPublicKey publicKey,
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
byte[] signature) implements SignedPreKey<ECPublicKey> {
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(type="string", description="""
The signature of the serialized `publicKey` with the account (or phone-number identity)'s identity key, base64-encoded.
""")
byte[] signature) implements SignedPreKey<ECPublicKey> {
@Override
public byte[] serializedPublicKey() {

View File

@ -7,21 +7,34 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.kem.KEMPublicKey;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.KEMPublicKeyAdapter;
import java.util.Arrays;
import java.util.Objects;
public record KEMSignedPreKey(long keyId,
public record KEMSignedPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24. The owner of this key must be able to determine from the key ID whether this represents
a single-use or last-resort key, but another party should *not* be able to tell.
""")
long keyId,
@JsonSerialize(using = KEMPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = KEMPublicKeyAdapter.Deserializer.class)
KEMPublicKey publicKey,
@JsonSerialize(using = KEMPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = KEMPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's Kyber1024 public key format and then base64-encoded.
""")
KEMPublicKey publicKey,
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
byte[] signature) implements SignedPreKey<KEMPublicKey> {
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(type="string", description="""
The signature of the serialized `publicKey` with the account (or phone-number identity)'s identity key, base64-encoded.
""")
byte[] signature) implements SignedPreKey<KEMPublicKey> {
@Override
public byte[] serializedPublicKey() {

View File

@ -6,6 +6,7 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
@ -24,10 +25,12 @@ public record PhoneNumberIdentityKeyDistributionRequest(
@NotNull
@Valid
@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keysManager
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.""")
@ArraySchema(
arraySchema=@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.
"""))
List<@NotNull @Valid IncomingMessage> deviceMessages,
@NotNull
@ -47,7 +50,7 @@ public record PhoneNumberIdentityKeyDistributionRequest(
@NotNull
@Valid
@Schema(description="The new registration ID to use for the phone-number identity of each device")
@Schema(description="The new registration ID to use for the phone-number identity of each device, including this one.")
Map<Long, Integer> pniRegistrationIds) {
@AssertTrue