LocalAccount.java

  1. /*
  2.  * The coLAB project
  3.  * Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
  4.  *
  5.  * Licensed under the MIT License
  6.  */
  7. package ch.colabproject.colab.api.model.user;

  8. import ch.colabproject.colab.api.Helper;
  9. import ch.colabproject.colab.api.exceptions.ColabMergeException;
  10. import ch.colabproject.colab.api.model.ColabEntity;
  11. import javax.json.bind.annotation.JsonbTransient;
  12. import javax.persistence.Column;
  13. import javax.persistence.Entity;
  14. import javax.persistence.EnumType;
  15. import javax.persistence.Enumerated;
  16. import javax.persistence.Index;
  17. import javax.persistence.NamedQuery;
  18. import javax.persistence.Table;
  19. import javax.validation.constraints.Email;
  20. import javax.validation.constraints.NotNull;
  21. import javax.validation.constraints.Size;

  22. /**
  23.  * Password based authentication.
  24.  *
  25.  * @author maxence
  26.  */
  27. @Entity
  28. @NamedQuery(name = "LocalAccount.findByEmail",
  29.     query = "SELECT a from LocalAccount a where a.email = :email")
  30. @Table(
  31.     // make sure to have JOINED inheritance, otherwise indexes and constraints will be ignored!
  32.     indexes = {
  33.         @Index(columnList = "email", unique = true)
  34.     })
  35. public class LocalAccount extends Account {

  36.     private static final long serialVersionUID = 1L;

  37.     // ---------------------------------------------------------------------------------------------
  38.     // fields
  39.     // ---------------------------------------------------------------------------------------------

  40.     /**
  41.      * username-like email address
  42.      */
  43.     @Size(max = 255)
  44.     @Email
  45.     @NotNull
  46.     private String email;

  47.     /**
  48.      * salt+password hash. Hashed with currentDbHashMethod and dbSalt.
  49.      */
  50.     @JsonbTransient
  51.     @NotNull
  52.     private byte[] hashedPassword;

  53.     /**
  54.      * Has the email address been verified with a VerificationToken ?
  55.      */
  56.     @NotNull
  57.     private Boolean verified;

  58.     /**
  59.      * Salt to used before hashing the password
  60.      */
  61.     @JsonbTransient
  62.     @NotNull
  63.     private byte[] dbSalt;

  64.     /**
  65.      * Hash method to use to hashedPassword the salt+password
  66.      */
  67.     @Column(length = 100)
  68.     @Enumerated(value = EnumType.STRING)
  69.     @JsonbTransient
  70.     @NotNull
  71.     private HashMethod currentDbHashMethod;

  72.     /**
  73.      * New hash method to use to hash the salt+password. If not null, rotate hash method on next
  74.      * successful authentication
  75.      */
  76.     @Column(length = 100)
  77.     @Enumerated(value = EnumType.STRING)
  78.     @JsonbTransient
  79.     private HashMethod nextDbHashMethod;

  80.     /**
  81.      * Salt the client shall use to before hashing its password. Salt is hex-encoded byte array
  82.      */
  83.     @Size(max = 255)
  84.     @NotNull
  85.     @JsonbTransient
  86.     private String clientSalt;

  87.     /**
  88.      * New salt the client shall use to prefix its password before hashing it.
  89.      * <p>
  90.      * In case this is not null, client shall send two hashes. First one is the plain_password
  91.      * prefixed with clientSalt and hashed with currentClientHashMethod (to authenticate), second is
  92.      * its password prefixed with this new salt and hashed with nextClientHashMethod if set, current
  93.      * otherwise. successful authentication (to rotate salt and/or method)
  94.      */
  95.     @Size(max = 255)
  96.     @JsonbTransient
  97.     private String newClientSalt;

  98.     /**
  99.      * Hash method the client shall use to hashedPassword the clientSalt+plain_password
  100.      */
  101.     @Column(length = 100)
  102.     @Enumerated(value = EnumType.STRING)
  103.     @NotNull
  104.     @JsonbTransient
  105.     private HashMethod currentClientHashMethod;

  106.     /**
  107.      * New hash method the client shall use to hash its clientSalt+plain_password.
  108.      * <p>
  109.      * In case this is not null, client shall send two hashes. First one is its plain_password
  110.      * prefixed clientSalt and hashed with currentClientHashMethod (to authenticate), second is its
  111.      * password prefixed with the new_salt (if set)or the current salt and hashed with this method
  112.      */
  113.     @Column(length = 100)
  114.     @Enumerated(value = EnumType.STRING)
  115.     @JsonbTransient
  116.     private HashMethod nextClientHashMethod;

  117.     // ---------------------------------------------------------------------------------------------
  118.     // getters and setters
  119.     // ---------------------------------------------------------------------------------------------

  120.     /**
  121.      * @return email associated with this account
  122.      */
  123.     public String getEmail() {
  124.         return email;
  125.     }

  126.     /**
  127.      * update email. If the email is not the same, this account will not be verified any longer
  128.      *
  129.      * @param email new email to use.
  130.      */
  131.     public void setEmail(String email) {
  132.         if (Helper.isEmailAddress(email)) { // FIXME sandra see with Maxence if it is useful to do
  133.                                             // it here

  134.             if (!email.equals(this.email)) {
  135.                 this.verified = false;
  136.             }
  137.             this.email = email;
  138.         }
  139.     }

  140.     /**
  141.      * get the stored hashedPassword to challenge authentication against
  142.      *
  143.      * @return hashedPassword hashedPassword to challenge authentication against
  144.      */
  145.     public byte[] getHashedPassword() {
  146.         return hashedPassword;
  147.     }

  148.     /**
  149.      * Update hashedPassword
  150.      *
  151.      * @param hashedPassword new hashedPassword
  152.      */
  153.     public void setHashedPassword(byte[] hashedPassword) {
  154.         this.hashedPassword = hashedPassword;
  155.     }

  156.     /**
  157.      * has the email address been verified ?
  158.      *
  159.      * @return true if the account is verified
  160.      */
  161.     public Boolean isVerified() {
  162.         return verified;
  163.     }

  164.     /**
  165.      * Set if the account has been verified or not
  166.      *
  167.      * @param verified yes or no ?
  168.      */
  169.     public void setVerified(Boolean verified) {
  170.         this.verified = verified;
  171.     }

  172.     /**
  173.      * @return the salt to used server-side
  174.      */
  175.     public byte[] getDbSalt() {
  176.         return dbSalt;
  177.     }

  178.     /**
  179.      * Update the server-side hashedPassword
  180.      *
  181.      * @param dbSalt new server-side salt
  182.      */
  183.     public void setDbSalt(byte[] dbSalt) {
  184.         this.dbSalt = dbSalt;
  185.     }

  186.     /**
  187.      * @return the current hashedPassword method to used to hashedPassword provided password
  188.      */
  189.     public HashMethod getCurrentDbHashMethod() {
  190.         return currentDbHashMethod;
  191.     }

  192.     /**
  193.      * Set the method to use to hashedPassword provided password
  194.      *
  195.      * @param currentDbHashMethod hashedPassword method
  196.      */
  197.     public void setCurrentDbHashMethod(HashMethod currentDbHashMethod) {
  198.         this.currentDbHashMethod = currentDbHashMethod;
  199.     }

  200.     /**
  201.      * @return the next hashedPassword method to use. If not null, hashedPassword methods will be
  202.      *         rotated on next authentication
  203.      */
  204.     public HashMethod getNextDbHashMethod() {
  205.         return nextDbHashMethod;
  206.     }

  207.     /**
  208.      * change the next hashedPassword method to used
  209.      *
  210.      * @param nextDbHashMethod next hashedPassword method
  211.      */
  212.     public void setNextDbHashMethod(HashMethod nextDbHashMethod) {
  213.         this.nextDbHashMethod = nextDbHashMethod;
  214.     }

  215.     /**
  216.      * @return the salt the user shall use before client-side plain_password hashedPassword
  217.      */
  218.     public String getClientSalt() {
  219.         return clientSalt;
  220.     }

  221.     /**
  222.      * Update the salt the client shall use
  223.      *
  224.      * @param clientSalt client salt
  225.      */
  226.     public void setClientSalt(String clientSalt) {
  227.         this.clientSalt = clientSalt;
  228.     }

  229.     /**
  230.      * @return the salt to use to rotate authentication
  231.      */
  232.     public String getNewClientSalt() {
  233.         return newClientSalt;
  234.     }

  235.     /**
  236.      * set a next client-side salt
  237.      *
  238.      * @param newClientSalt new salt we want the client to use
  239.      */
  240.     public void setNewClientSalt(String newClientSalt) {
  241.         this.newClientSalt = newClientSalt;
  242.     }

  243.     /**
  244.      * @return the hashedPassword method the client shall use to hashedPassword its
  245.      *         salt+plain_password
  246.      */
  247.     public HashMethod getCurrentClientHashMethod() {
  248.         return currentClientHashMethod;
  249.     }

  250.     /**
  251.      * CHange hashedPassword method the client shall use
  252.      *
  253.      * @param currentClientHashMethod hashedPassword method the client shall use
  254.      */
  255.     public void setCurrentClientHashMethod(HashMethod currentClientHashMethod) {
  256.         this.currentClientHashMethod = currentClientHashMethod;
  257.     }

  258.     /**
  259.      * @return the next hashedPassword method the client shall use
  260.      */
  261.     public HashMethod getNextClientHashMethod() {
  262.         return nextClientHashMethod;
  263.     }

  264.     /**
  265.      * Update the next client-side hashedPassword method
  266.      *
  267.      * @param nextClientHashMethod new next client hashedPassword method
  268.      */
  269.     public void setNextClientHashMethod(HashMethod nextClientHashMethod) {
  270.         this.nextClientHashMethod = nextClientHashMethod;
  271.     }

  272.     // ---------------------------------------------------------------------------------------------
  273.     // concerning the whole class
  274.     // ---------------------------------------------------------------------------------------------

  275.     @Override
  276.     public void mergeToUpdate(ColabEntity other) throws ColabMergeException {
  277.         if (other instanceof LocalAccount) {
  278.             LocalAccount o = (LocalAccount) other;
  279.             this.setDeletionStatus(o.getDeletionStatus());
  280.             this.setEmail(o.getEmail());
  281.             // the others fields cannot be changed by a simple update
  282.         } else {
  283.             throw new ColabMergeException(this, other);
  284.         }
  285.     }

  286.     @Override
  287.     public String toString() {
  288.         return "LocalAccount{" + "id=" + this.getId() + ", deletion=" + getDeletionStatus()
  289.             + ", email=" + email + ", verified=" + verified + '}';
  290.     }

  291. }