From da395757d7130a348641e1dea3b2be3e90c7293a Mon Sep 17 00:00:00 2001 From: FireMasterK <20838718+FireMasterK@users.noreply.github.com> Date: Mon, 5 Apr 2021 21:43:25 +0530 Subject: [PATCH] Basic hibernate structure. --- build.gradle | 3 +- .../java/me/kavin/piped/consts/Constants.java | 4 - .../me/kavin/piped/utils/PasswordHash.java | 152 ++++++++++++++++++ .../me/kavin/piped/utils/obj/db/User.java | 80 +++++++++ .../me/kavin/piped/utils/obj/db/Video.java | 140 ++++++++++++++++ src/main/resources/hibernate.cfg.xml | 12 ++ 6 files changed, 386 insertions(+), 5 deletions(-) create mode 100644 src/main/java/me/kavin/piped/utils/PasswordHash.java create mode 100644 src/main/java/me/kavin/piped/utils/obj/db/User.java create mode 100644 src/main/java/me/kavin/piped/utils/obj/db/Video.java create mode 100644 src/main/resources/hibernate.cfg.xml diff --git a/build.gradle b/build.gradle index 63c52b8..de433b0 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,6 @@ dependencies { implementation 'it.unimi.dsi:fastutil-core:8.5.4' implementation 'commons-codec:commons-codec:1.15' implementation 'org.bouncycastle:bcprov-jdk15on:1.69' - implementation 'org.mongodb:mongodb-driver-sync:4.2.2' implementation 'com.github.TiA4f8R.NewPipeExtractor:NewPipeExtractor:df53170023ff5b97ad1d7ccc74bfce74c3998bcc' implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' implementation 'com.fasterxml.jackson.core:jackson-core:2.12.4' @@ -32,6 +31,8 @@ dependencies { implementation 'io.activej:activej-boot:4.3' implementation 'io.activej:activej-specializer:4.3' implementation 'io.activej:activej-launchers-http:4.3' + implementation 'org.postgresql:postgresql:42.2.19' + implementation 'org.hibernate:hibernate-core:5.4.30.Final' implementation 'net.java.dev.jna:jna-platform:5.8.0' } diff --git a/src/main/java/me/kavin/piped/consts/Constants.java b/src/main/java/me/kavin/piped/consts/Constants.java index 05c811a..fba82b1 100644 --- a/src/main/java/me/kavin/piped/consts/Constants.java +++ b/src/main/java/me/kavin/piped/consts/Constants.java @@ -11,7 +11,6 @@ import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; import com.fasterxml.jackson.databind.ObjectMapper; -import com.mongodb.client.MongoClient; import me.kavin.piped.utils.PageMixin; @@ -36,8 +35,6 @@ public class Constants { .version(Version.HTTP_2).build(); // public static final HttpClient h3client = Http3ClientBuilder.newBuilder().followRedirects(Redirect.NORMAL).build(); - public static final MongoClient mongoClient; - public static final ObjectMapper mapper = new ObjectMapper().addMixIn(Page.class, PageMixin.class); static { @@ -51,7 +48,6 @@ public class Constants { PROXY_PART = prop.getProperty("PROXY_PART"); CAPTCHA_BASE_URL = prop.getProperty("CAPTCHA_BASE_URL"); CAPTCHA_API_KEY = prop.getProperty("CAPTCHA_API_KEY"); - mongoClient = null/* MongoClients.create(prop.getProperty("MONGO_URI")) */; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/me/kavin/piped/utils/PasswordHash.java b/src/main/java/me/kavin/piped/utils/PasswordHash.java new file mode 100644 index 0000000..ebe11d9 --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/PasswordHash.java @@ -0,0 +1,152 @@ +package me.kavin.piped.utils; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +/* + * PBKDF2 salted password hashing. + * Author: havoc AT defuse.ca + * www: http://crackstation.net/hashing-security.htm + * source: https://gist.github.com/jtan189/3804290 + */ +public class PasswordHash { + public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA512"; + + // The following constants may be changed without breaking existing hashes. + public static final int SALT_BYTES = 64; + public static final int HASH_BYTES = 64; + public static final int PBKDF2_ITERATIONS = 1000; + + public static final int ITERATION_INDEX = 0; + public static final int SALT_INDEX = 1; + public static final int PBKDF2_INDEX = 2; + + /** + * Returns a salted PBKDF2 hash of the password. + * + * @param password the password to hash + * @return a salted PBKDF2 hash of the password + */ + public static String createHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { + return createHash(password.toCharArray()); + } + + /** + * Returns a salted PBKDF2 hash of the password. + * + * @param password the password to hash + * @return a salted PBKDF2 hash of the password + */ + public static String createHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException { + // Generate a random salt + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[SALT_BYTES]; + random.nextBytes(salt); + + // Hash the password + byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTES); + // format iterations:salt:hash + return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash); + } + + /** + * Validates a password using a hash. + * + * @param password the password to check + * @param goodHash the hash of the valid password + * @return true if the password is correct, false if not + */ + public static boolean validatePassword(String password, String goodHash) + throws NoSuchAlgorithmException, InvalidKeySpecException { + return validatePassword(password.toCharArray(), goodHash); + } + + /** + * Validates a password using a hash. + * + * @param password the password to check + * @param goodHash the hash of the valid password + * @return true if the password is correct, false if not + */ + public static boolean validatePassword(char[] password, String goodHash) + throws NoSuchAlgorithmException, InvalidKeySpecException { + // Decode the hash into its parameters + String[] params = goodHash.split(":"); + int iterations = Integer.parseInt(params[ITERATION_INDEX]); + byte[] salt = fromHex(params[SALT_INDEX]); + byte[] hash = fromHex(params[PBKDF2_INDEX]); + // Compute the hash of the provided password, using the same salt, + // iteration count, and hash length + byte[] testHash = pbkdf2(password, salt, iterations, hash.length); + // Compare the hashes in constant time. The password is correct if + // both hashes match. + return slowEquals(hash, testHash); + } + + /** + * Compares two byte arrays in length-constant time. This comparison method is + * used so that password hashes cannot be extracted from an on-line system using + * a timing attack and then attacked off-line. + * + * @param a the first byte array + * @param b the second byte array + * @return true if both byte arrays are the same, false if not + */ + private static boolean slowEquals(byte[] a, byte[] b) { + int diff = a.length ^ b.length; + for (int i = 0; i < a.length && i < b.length; i++) + diff |= a[i] ^ b[i]; + return diff == 0; + } + + /** + * Computes the PBKDF2 hash of a password. + * + * @param password the password to hash. + * @param salt the salt + * @param iterations the iteration count (slowness factor) + * @param bytes the length of the hash to compute in bytes + * @return the PBDKF2 hash of the password + */ + private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) + throws NoSuchAlgorithmException, InvalidKeySpecException { + PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8); + SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); + return skf.generateSecret(spec).getEncoded(); + } + + /** + * Converts a string of hexadecimal characters into a byte array. + * + * @param hex the hex string + * @return the hex string decoded into a byte array + */ + private static byte[] fromHex(String hex) { + byte[] binary = new byte[hex.length() / 2]; + for (int i = 0; i < binary.length; i++) { + binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); + } + return binary; + } + + /** + * Converts a byte array into a hexadecimal string. + * + * @param array the byte array to convert + * @return a length*2 character string encoding the byte array + */ + private static String toHex(byte[] array) { + BigInteger bi = new BigInteger(1, array); + String hex = bi.toString(16); + int paddingLength = (array.length * 2) - hex.length(); + if (paddingLength > 0) + return String.format("%0" + paddingLength + "d", 0) + hex; + else + return hex; + } +} \ No newline at end of file diff --git a/src/main/java/me/kavin/piped/utils/obj/db/User.java b/src/main/java/me/kavin/piped/utils/obj/db/User.java new file mode 100644 index 0000000..5b5c94b --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/obj/db/User.java @@ -0,0 +1,80 @@ +package me.kavin.piped.utils.obj.db; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.Table; + +@Entity +@Table(name = "users", indexes = { @Index(columnList = "id", name = "id_idx"), + @Index(columnList = "username", name = "username_idx") }) +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "username", unique = true, length = 24) + private String username; + + @Column(name = "password", columnDefinition = "text") + private String password; + + @ElementCollection + @CollectionTable(name = "users_subscribed", joinColumns = @JoinColumn(name = "subscriber"), indexes = @Index(columnList = "subscriber", name = "subscriber_idx")) + @Column(name = "channel", length = 30) + private List subscribed_ids; + + public User() { + } + + public User(String username, String password, List subscribed_ids) { + this.username = username; + this.password = password; + this.subscribed_ids = subscribed_ids; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getSubscribed() { + return subscribed_ids; + } + + public void setSubscribed(List subscribed_ids) { + this.subscribed_ids = subscribed_ids; + } +} diff --git a/src/main/java/me/kavin/piped/utils/obj/db/Video.java b/src/main/java/me/kavin/piped/utils/obj/db/Video.java new file mode 100644 index 0000000..c15eec5 --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/obj/db/Video.java @@ -0,0 +1,140 @@ +package me.kavin.piped.utils.obj.db; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; + +@Entity +@Table(name = "videos", indexes = { @Index(columnList = "id", name = "id_idx") }) +public class Video { + + @Id + @Column(name = "id", unique = true, length = 16) + private String id; + + @Column(name = "title", length = 100) + private String title; + + @Column(name = "views") + private long views; + + @Column(name = "duration") + private int duration; + + @Column(name = "uploaded") + private long uploaded; + + @Column(name = "uploader", length = 80) + private String uploader; + + @Column(name = "uploader_url", length = 30) + private String uploaderUrl; + + @Column(name = "uploader_avatar", length = 150) + private String uploaderAvatar; + + @Column(name = "verified") + private boolean verified; + + @Column(name = "thumbnail", length = 150) + private String thumbnail; + + public Video() { + } + + public Video(String id, String title, long views, int duration, long uploaded, String uploader, String uploaderUrl, + String uploaderAvatar, boolean verified, String thumbnail) { + this.id = id; + this.title = title; + this.views = views; + this.duration = duration; + this.uploaded = uploaded; + this.uploader = uploader; + this.uploaderUrl = uploaderUrl; + this.uploaderAvatar = uploaderAvatar; + this.verified = verified; + this.thumbnail = thumbnail; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public long getViews() { + return views; + } + + public void setViews(long views) { + this.views = views; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public long getUploaded() { + return uploaded; + } + + public void setUploaded(long uploaded) { + this.uploaded = uploaded; + } + + public String getUploader() { + return uploader; + } + + public void setUploader(String uploader) { + this.uploader = uploader; + } + + public String getUploaderUrl() { + return uploaderUrl; + } + + public void setUploaderUrl(String uploaderUrl) { + this.uploaderUrl = uploaderUrl; + } + + public String getUploaderAvatar() { + return uploaderAvatar; + } + + public void setUploaderAvatar(String uploaderAvatar) { + this.uploaderAvatar = uploaderAvatar; + } + + public boolean isVerified() { + return verified; + } + + public void setVerified(boolean verified) { + this.verified = verified; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } +} diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml new file mode 100644 index 0000000..ad221e0 --- /dev/null +++ b/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,12 @@ + + + + + update + + true + true + +