From a50971f8e279c624b71736849c8907417600ca47 Mon Sep 17 00:00:00 2001 From: bprize15 Date: Tue, 19 Nov 2024 10:18:42 -0500 Subject: [PATCH 1/4] update token model and db to support github token format --- .jhipster/Token.json | 4 ++ jhipster-jdl.jdl | 3 +- .../org/mskcc/cbio/oncokb/domain/Token.java | 70 ++++++++++++++++++- .../mskcc/cbio/oncokb/domain/TokenKey.java | 41 +++++++++++ .../oncokb/domain/enumeration/TokenType.java | 16 +++++ ...18184124_added_field_newToken_to_Token.xml | 8 +++ .../resources/config/liquibase/master.xml | 1 + 7 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java create mode 100644 src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/TokenType.java create mode 100644 src/main/resources/config/liquibase/changelog/20241118184124_added_field_newToken_to_Token.xml diff --git a/.jhipster/Token.json b/.jhipster/Token.json index f1b13b58a..f95832f3f 100644 --- a/.jhipster/Token.json +++ b/.jhipster/Token.json @@ -36,6 +36,10 @@ "fieldName": "renewable", "fieldType": "Boolean", "fieldValidateRules": ["required"] + }, + { + "fieldName": "newToken", + "fieldType": "String" } ], "changelogDate": "20190823204705", diff --git a/jhipster-jdl.jdl b/jhipster-jdl.jdl index d33ddd22d..f1947c306 100644 --- a/jhipster-jdl.jdl +++ b/jhipster-jdl.jdl @@ -4,7 +4,8 @@ entity Token { expiration Instant, usageLimit Integer, currentUsage Integer required, - renewable Boolean required + renewable Boolean required, + newToken String } entity TokenStats { diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java b/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java index ad58f95aa..fa6c51606 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.hibernate.annotations.Type; +import org.mskcc.cbio.oncokb.domain.enumeration.TokenType; import javax.persistence.*; import javax.validation.constraints.*; @@ -38,11 +39,15 @@ public class Token implements Serializable { @NotNull @Column(name = "current_usage", nullable = false) - private Integer currentUsage = 0; + private Integer currentUsage; @NotNull @Column(name = "renewable", nullable = false) - private Boolean renewable = true; + private Boolean renewable; + + @Convert(converter = TokenKeyConverter.class) + @Column(name = "new_token") + private TokenKey newToken; @ManyToOne @JsonIgnoreProperties(value = "tokens", allowSetters = true) @@ -135,6 +140,19 @@ public void setRenewable(Boolean renewable) { this.renewable = renewable; } + public TokenKey getNewToken() { + return newToken; + } + + public Token newToken(TokenKey newToken) { + this.newToken = newToken; + return this; + } + + public void setNewToken(TokenKey newToken) { + this.newToken = newToken; + } + public User getUser() { return user; } @@ -176,6 +194,54 @@ public String toString() { ", usageLimit=" + getUsageLimit() + ", currentUsage=" + getCurrentUsage() + ", renewable='" + isRenewable() + "'" + + ", newToken='" + getNewToken() + "'" + "}"; } } + +class TokenKeyConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(TokenKey tokenKey) { + if (tokenKey.getTokenType() == null || tokenKey.getToken() == null || tokenKey.getChecksum() == null) { + return null; + } + if (tokenKey.getToken().length() != TokenKey.TOKEN_CHAR_LENGTH || tokenKey.getChecksum().length() != TokenKey.CHECKSUM_CHAR_LENGTH) { + return null; + } + return tokenKey.getTokenType().getType() + "_" + tokenKey.getToken() + tokenKey.getChecksum(); + } + + @Override + public TokenKey convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + String[] parts = dbData.split("_"); + if (parts.length != 2) { + return null; + } + + + TokenKey tokenKey = new TokenKey(); + String type = parts[0]; + if (type.equals(TokenType.SERVICE.getType())) { + tokenKey.setTokenType(TokenType.SERVICE); + } else if (type.equals(TokenType.USER.getType())) { + tokenKey.setTokenType(TokenType.USER); + } else { + return null; + } + + String tokenAndChecksum = parts[1]; + if (tokenAndChecksum.length() == TokenKey.TOKEN_CHAR_LENGTH + TokenKey.CHECKSUM_CHAR_LENGTH) { + String token = tokenAndChecksum.substring(0, TokenKey.TOKEN_CHAR_LENGTH); + String checksum = tokenAndChecksum.substring(TokenKey.TOKEN_CHAR_LENGTH); + tokenKey.setToken(token); + tokenKey.setChecksum(checksum); + } else { + return null; + } + + return tokenKey; + } +} \ No newline at end of file diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java new file mode 100644 index 000000000..0c1f59519 --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java @@ -0,0 +1,41 @@ +package org.mskcc.cbio.oncokb.domain; + +import java.io.Serializable; + +import org.mskcc.cbio.oncokb.domain.enumeration.TokenType; + +public class TokenKey implements Serializable { + public static int TOKEN_CHAR_LENGTH = 30; + + public static int CHECKSUM_CHAR_LENGTH = 6; + + private TokenType tokenType; + + private String token; + + private String checksum; + + public TokenType getTokenType() { + return tokenType; + } + + public void setTokenType(TokenType tokenType) { + this.tokenType = tokenType; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getChecksum() { + return checksum; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/TokenType.java b/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/TokenType.java new file mode 100644 index 000000000..b54ae5dcf --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/TokenType.java @@ -0,0 +1,16 @@ +package org.mskcc.cbio.oncokb.domain.enumeration; + +public enum TokenType { + USER("ocku"), + SERVICE("okbs"); + + String type; + + TokenType(String type) { + this.type = type; + } + + public String getType() { + return this.type; + } +} diff --git a/src/main/resources/config/liquibase/changelog/20241118184124_added_field_newToken_to_Token.xml b/src/main/resources/config/liquibase/changelog/20241118184124_added_field_newToken_to_Token.xml new file mode 100644 index 000000000..dac25f73e --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20241118184124_added_field_newToken_to_Token.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index b90a7fbb7..68ea5b5a5 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -27,6 +27,7 @@ + From 3b80ff2206265b291a4f6f2517bdfb4accdc0ea6 Mon Sep 17 00:00:00 2001 From: bprize15 Date: Tue, 19 Nov 2024 17:03:52 -0500 Subject: [PATCH 2/4] add new token generation logic --- .../mskcc/cbio/oncokb/domain/TokenKey.java | 31 +++++++++++++++++++ .../oncokb/security/uuid/TokenProvider.java | 3 ++ 2 files changed, 34 insertions(+) diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java index 0c1f59519..f364fe8ae 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java @@ -1,9 +1,14 @@ package org.mskcc.cbio.oncokb.domain; import java.io.Serializable; +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.zip.CRC32; import org.mskcc.cbio.oncokb.domain.enumeration.TokenType; +import io.seruco.encoding.base62.Base62; + public class TokenKey implements Serializable { public static int TOKEN_CHAR_LENGTH = 30; @@ -15,6 +20,32 @@ public class TokenKey implements Serializable { private String checksum; + public static TokenKey generate(TokenType type) { + TokenKey tokenKey = new TokenKey(); + tokenKey.setTokenType(type); + + Base62 base62 = Base62.createInstance(); + SecureRandom secureRandom = new SecureRandom(); + + byte[] bytes = new byte[24]; + secureRandom.nextBytes(bytes); + String token = new String(base62.encode(bytes)); + tokenKey.setToken(token); + + CRC32 crc32 = new CRC32(); + crc32.update(bytes); + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(crc32.getValue()); + String checksum = new String(base62.encode(buffer.array())); + tokenKey.setChecksum(checksum.substring(checksum.length() - TokenKey.CHECKSUM_CHAR_LENGTH)); + + return tokenKey; + } + + public boolean validateChecksum() { + return false; + } + public TokenType getTokenType() { return tokenType; } diff --git a/src/main/java/org/mskcc/cbio/oncokb/security/uuid/TokenProvider.java b/src/main/java/org/mskcc/cbio/oncokb/security/uuid/TokenProvider.java index 08ce324aa..b01c90c22 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/security/uuid/TokenProvider.java +++ b/src/main/java/org/mskcc/cbio/oncokb/security/uuid/TokenProvider.java @@ -2,7 +2,9 @@ import org.mskcc.cbio.oncokb.domain.Authority; import org.mskcc.cbio.oncokb.domain.Token; +import org.mskcc.cbio.oncokb.domain.TokenKey; import org.mskcc.cbio.oncokb.domain.User; +import org.mskcc.cbio.oncokb.domain.enumeration.TokenType; import org.mskcc.cbio.oncokb.repository.UserRepository; import org.mskcc.cbio.oncokb.security.AuthoritiesConstants; import org.mskcc.cbio.oncokb.security.SecurityUtils; @@ -74,6 +76,7 @@ private Token getNewToken(Set authorities, Optional definedE token.setExpiration(expirationTime); } token.setToken(UUID.randomUUID()); + token.setNewToken(TokenKey.generate(TokenType.USER)); return token; } From 144af1452d4221c206e257fbec469e20aa9c0208 Mon Sep 17 00:00:00 2001 From: bprize15 Date: Wed, 20 Nov 2024 13:31:01 -0500 Subject: [PATCH 3/4] refactor token generation --- .../org/mskcc/cbio/oncokb/domain/Token.java | 4 +- .../mskcc/cbio/oncokb/domain/TokenKey.java | 44 +++++++++++++------ .../cbio/oncokb/domain/TokenKeyTest.java | 31 +++++++++++++ 3 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/test/java/org/mskcc/cbio/oncokb/domain/TokenKeyTest.java diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java b/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java index fa6c51606..009f19b9a 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/Token.java @@ -39,11 +39,11 @@ public class Token implements Serializable { @NotNull @Column(name = "current_usage", nullable = false) - private Integer currentUsage; + private Integer currentUsage = 0; @NotNull @Column(name = "renewable", nullable = false) - private Boolean renewable; + private Boolean renewable = true; @Convert(converter = TokenKeyConverter.class) @Column(name = "new_token") diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java index f364fe8ae..ec3b54045 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java @@ -1,19 +1,19 @@ package org.mskcc.cbio.oncokb.domain; import java.io.Serializable; -import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.zip.CRC32; +import org.apache.commons.lang3.StringUtils; import org.mskcc.cbio.oncokb.domain.enumeration.TokenType; -import io.seruco.encoding.base62.Base62; - public class TokenKey implements Serializable { public static int TOKEN_CHAR_LENGTH = 30; public static int CHECKSUM_CHAR_LENGTH = 6; + private static String BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private TokenType tokenType; private String token; @@ -24,24 +24,40 @@ public static TokenKey generate(TokenType type) { TokenKey tokenKey = new TokenKey(); tokenKey.setTokenType(type); - Base62 base62 = Base62.createInstance(); - SecureRandom secureRandom = new SecureRandom(); + CRC32 crc32 = new CRC32(); - byte[] bytes = new byte[24]; - secureRandom.nextBytes(bytes); - String token = new String(base62.encode(bytes)); + String token = generateToken(); tokenKey.setToken(token); - CRC32 crc32 = new CRC32(); - crc32.update(bytes); - ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); - buffer.putLong(crc32.getValue()); - String checksum = new String(base62.encode(buffer.array())); - tokenKey.setChecksum(checksum.substring(checksum.length() - TokenKey.CHECKSUM_CHAR_LENGTH)); + crc32.update(token.getBytes()); + String base62Checksum = toBase62(crc32.getValue()); + if (base62Checksum.length() < CHECKSUM_CHAR_LENGTH) { + base62Checksum = StringUtils.repeat('0', CHECKSUM_CHAR_LENGTH - base62Checksum.length()); + } + tokenKey.setChecksum(base62Checksum); return tokenKey; } + private static String generateToken() { + SecureRandom secureRandom = new SecureRandom(); + StringBuilder token = new StringBuilder(); + for (int i = 0; i < TOKEN_CHAR_LENGTH; i++) { + token.append(BASE62_CHARS.charAt(secureRandom.nextInt(BASE62_CHARS.length()))); + } + return token.toString(); + } + + public static String toBase62(long val) { + StringBuffer sb = new StringBuffer(); + while(val > 0) { + int remainder = (int) (val % 62); + val = val / 62; + sb.insert(0, BASE62_CHARS.charAt((int) remainder)); + } + return sb.toString(); + } + public boolean validateChecksum() { return false; } diff --git a/src/test/java/org/mskcc/cbio/oncokb/domain/TokenKeyTest.java b/src/test/java/org/mskcc/cbio/oncokb/domain/TokenKeyTest.java new file mode 100644 index 000000000..c4a25a1da --- /dev/null +++ b/src/test/java/org/mskcc/cbio/oncokb/domain/TokenKeyTest.java @@ -0,0 +1,31 @@ +package org.mskcc.cbio.oncokb.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.ByteBuffer; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +public class TokenKeyTest { + @Test + public void toBase62() { + String test = "d"; + assertThat(TokenKey.toBase62(stringToLong(test))).isEqualTo("1c"); + + test = "hello"; + assertThat(TokenKey.toBase62(stringToLong(test))).isEqualTo("7tQLFHz"); + + test = "oncokb"; + assertThat(TokenKey.toBase62(122519905332066L)).isEqualTo("Yn1xclvu"); + } + + private long stringToLong(String str) { + if (str.length() < Long.BYTES) { + str = StringUtils.repeat('\0', Long.BYTES - str.length()) + str; + } + + ByteBuffer buffer = ByteBuffer.wrap(str.getBytes()); + return buffer.getLong(0); + } +} From c65f46c7f5bece62c3da56715113c674b0e2c091 Mon Sep 17 00:00:00 2001 From: bprize15 Date: Wed, 20 Nov 2024 13:47:12 -0500 Subject: [PATCH 4/4] finalize new token generation --- src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java index ec3b54045..904df66e5 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/TokenKey.java @@ -58,10 +58,6 @@ public static String toBase62(long val) { return sb.toString(); } - public boolean validateChecksum() { - return false; - } - public TokenType getTokenType() { return tokenType; }