experimental access code for RbacUserEntity

This commit is contained in:
Michael Hoennig 2022-09-03 12:31:56 +02:00
parent 1451e7b661
commit 2abe88eb15
2 changed files with 102 additions and 1 deletions

View File

@ -3,7 +3,12 @@ package net.hostsharing.hsadminng.rbac.rbacuser;
import lombok.*; import lombok.*;
import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.Immutable;
import javax.persistence.*; import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@ -16,8 +21,35 @@ import java.util.UUID;
@AllArgsConstructor @AllArgsConstructor
public class RbacUserEntity { public class RbacUserEntity {
private static final int MAX_VALIDITY_DAYS = 21;
private static DateTimeFormatter DATE_FORMAT_WITH_FULLHOUR = DateTimeFormatter.ofPattern("MM-dd-yyyy HH");
@Id @Id
private UUID uuid; private UUID uuid;
private String name; private String name;
public String generateAccessCode() {
return generateAccessCode(LocalDateTime.now());
}
public boolean isValidAccessCode(final String accessCode, final int validityHours) {
if (validityHours > 24 * MAX_VALIDITY_DAYS) {
throw new IllegalArgumentException("Max validity (%s days) exceeded.".formatted(MAX_VALIDITY_DAYS));
}
if (generateAccessCode(LocalDateTime.now().minus(validityHours, ChronoUnit.HOURS)).equals(accessCode)) {
return true;
}
if (validityHours < 0) {
return false;
}
return isValidAccessCode(accessCode, validityHours - 1);
}
String generateAccessCode(final LocalDateTime timestamp) {
final var compound = name + ":" + uuid + ":" + timestamp.format(DATE_FORMAT_WITH_FULLHOUR);
final var code = String.valueOf(1000000 + Math.abs(compound.hashCode()) % 100000);
return code.substring(1, 4) + ":" + code.substring(4, 7);
}
} }

View File

@ -0,0 +1,69 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class RbacUserEntityUnitTest {
RbacUserEntity givenUser = new RbacUserEntity(UUID.randomUUID(), "test@example.org");
@Test
void generatedAccessCodeMatchesDefinedPattern() {
final var givenAccessCode = givenUser.generateAccessCode();
final var actual = givenAccessCode.matches("[0-9]{3}:[0-9]{3}");
assertThat(actual).isTrue();
}
@Test
void freshAccessCodeIsValid() {
final var givenAccessCode = givenUser.generateAccessCode();
final var actual = givenUser.isValidAccessCode(givenAccessCode, 4);
assertThat(actual).isTrue();
}
@Test
void recentEnoughAccessCodeIsValid() {
final var givenAccessCode = givenUser.generateAccessCode(LocalDateTime.now().minus(4, ChronoUnit.HOURS));
final var actual = givenUser.isValidAccessCode(givenAccessCode, 4);
assertThat(actual).isTrue();
}
@Test
void outdatedEnoughAccessCodeIsNotValid() {
final var givenAccessCode = givenUser.generateAccessCode(LocalDateTime.now().minus(5, ChronoUnit.HOURS));
final var actual = givenUser.isValidAccessCode(givenAccessCode, 4);
assertThat(actual).isFalse();
}
@Test
void noExceptionIsThrowIfMaxValidityIsNotExceeded() {
final var givenAccessCode = givenUser.generateAccessCode();
givenUser.isValidAccessCode(givenAccessCode, 24 * 21);
}
@Test
void illegalArgumentExceptionIsThrowIfMaxValidityIsExceeded() {
final var givenAccessCode = givenUser.generateAccessCode();
assertThatThrownBy(() -> {
givenUser.isValidAccessCode(givenAccessCode, 24 * 21 + 1);
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Max validity (21 days) exceeded.");
}
}