Support for first/last profile name length

This commit is contained in:
Moxie Marlinspike 2020-01-13 18:55:04 -08:00
parent 4468b5a2e4
commit 8a9fed64f2
4 changed files with 108 additions and 1 deletions

View File

@ -24,6 +24,7 @@ import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.UsernamesManager; import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.util.ExactlySize;
import org.whispersystems.textsecuregcm.util.Pair; import org.whispersystems.textsecuregcm.util.Pair;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -156,7 +157,7 @@ public class ProfileController {
@PUT @PUT
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/name/{name}") @Path("/name/{name}")
public void setProfile(@Auth Account account, @PathParam("name") @UnwrapValidatedValue(true) @Length(min = 72,max= 72) Optional<String> name) { public void setProfile(@Auth Account account, @PathParam("name") @UnwrapValidatedValue(true) @ExactlySize({72, 108}) Optional<String> name) {
account.setProfileName(name.orElse(null)); account.setProfileName(name.orElse(null));
accountsManager.update(account); accountsManager.update(account);
} }

View File

@ -0,0 +1,34 @@
package org.whispersystems.textsecuregcm.util;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = ExactlySizeValidator.class)
@Documented
public @interface ExactlySize {
String message() default "{org.whispersystems.textsecuregcm.util.ExactlySize." +
"message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] value();
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
@interface List {
ExactlySize[] value();
}
}

View File

@ -0,0 +1,29 @@
package org.whispersystems.textsecuregcm.util;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ExactlySizeValidator implements ConstraintValidator<ExactlySize, String> {
private int[] permittedSizes;
@Override
public void initialize(ExactlySize exactlySize) {
this.permittedSizes = exactlySize.value();
}
@Override
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
int objectLength;
if (object == null) objectLength = 0;
else objectLength = object.length();
for (int permittedSize : permittedSizes) {
if (permittedSize == objectLength) return true;
}
return false;
}
}

View File

@ -5,7 +5,9 @@ import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier; import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration; import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
@ -20,6 +22,7 @@ import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.Optional; import java.util.Optional;
@ -89,6 +92,8 @@ public class ProfileControllerTest {
when(accountsManager.get(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount)); when(accountsManager.get(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount));
when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER)))).thenReturn(Optional.of(capabilitiesAccount)); when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER)))).thenReturn(Optional.of(capabilitiesAccount));
Mockito.clearInvocations(accountsManager);
} }
@Test @Test
@ -209,4 +214,42 @@ public class ProfileControllerTest {
assertThat(profile.getCapabilities().isUuid()).isTrue(); assertThat(profile.getCapabilities().isUuid()).isTrue();
} }
@Test
public void testSetProfileName() {
Response response = resources.getJerseyTest()
.target("/v1/profile/name/123456789012345678901234567890123456789012345678901234567890123456789012")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(204);
verify(accountsManager, times(1)).update(any(Account.class));
}
@Test
public void testSetProfileNameExtended() {
Response response = resources.getJerseyTest()
.target("/v1/profile/name/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(204);
verify(accountsManager, times(1)).update(any(Account.class));
}
@Test
public void testSetProfileNameWrongSize() {
Response response = resources.getJerseyTest()
.target("/v1/profile/name/1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(400);
verifyNoMoreInteractions(accountsManager);
}
} }