/* * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved. */ package net.curisit.securis.db; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQuery; import jakarta.persistence.Table; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; /** * User *

* Application user with bitmask-based roles and membership in organizations * and applications. Exposes convenience JSON properties to fetch/set related * entity IDs without fetching full entities. * * Mapping details: * - Table: user * - ManyToMany organizations via user_organization * - ManyToMany applications via user_application * - Named queries: list-users, get-user, auth-user, delete-all-users * * Roles: * - Stored as integer bitmask; see {@link Rol}. * * @author JRA * Last reviewed by JRA on Oct 5, 2025. */ @JsonAutoDetect @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) @Entity @Table(name = "user") @NamedQueries({ @NamedQuery(name = "list-users", query = "SELECT u FROM User u"), @NamedQuery(name = "get-user", query = "SELECT u FROM User u where u.username = :username"), @NamedQuery(name = "auth-user", query = "SELECT u FROM User u where u.username = :username and u.password = :password"), @NamedQuery(name = "delete-all-users", query = "delete FROM User u") }) public class User implements Serializable { private static final long serialVersionUID = 1L; /** Username (PK). */ @Id private String username; /** Password hash/string (not exposed in JSON). */ private String password; @JsonProperty("first_name") @Column(name = "first_name") private String firstName; @JsonProperty("last_name") @Column(name = "last_name") private String lastName; /** Roles bitmask (see Rol constants). */ private int roles; @Column(name = "last_login") private Date lastLogin; @Column(name = "modification_timestamp") private Date modificationTimestamp; @Column(name = "creation_timestamp") @JsonProperty("creation_timestamp") private Date creationTimestamp; private String lang; private String email; @JsonIgnore @ManyToMany @JoinTable(name = "user_organization", joinColumns = { @JoinColumn(name = "username", referencedColumnName = "username") }, inverseJoinColumns = { @JoinColumn(name = "organization_id", referencedColumnName = "id") }) private Set organizations; @JsonIgnore @ManyToMany @JoinTable(name = "user_application", joinColumns = { @JoinColumn(name = "username", referencedColumnName = "username") }, inverseJoinColumns = { @JoinColumn(name = "application_id", referencedColumnName = "id") }) private Set applications; // -------- Getters & setters -------- /** * getUsername

* Return username (PK). * * @return username */ public String getUsername() { return username; } /** * setUsername

* Set username (PK). * * @param username */ public void setUsername(String username) { this.username = username; } /** * getDummyPassword

* Forces password to be omitted in JSON responses. * * @return always null */ @JsonProperty("password") public String getDummyPassword() { return null; } /** * getPassword

* Return raw/hashed password (internal use). * * @return password */ public String getPassword() { return password; } /** * setPassword

* Set raw/hashed password (internal use). * * @param password */ public void setPassword(String password) { this.password = password; } /** * getRoles

* Return list of individual role flags contained in the bitmask. * * @return list of role integers or null if no roles */ public List getRoles() { if (roles == 0) return null; List aux = new ArrayList<>(); for (int rol : Rol.ALL) { if ((roles & rol) != 0) aux.add(rol); } return aux; } /** * setRoles

* Set the roles bitmask from a list of role flags. * * @param roles list of flags */ public void setRoles(List roles) { this.roles = 0; if (roles != null) { for (Integer rol : roles) this.roles |= rol; } } /** * getFirstName

* Return first name. * * @return firstName */ public String getFirstName() { return firstName; } /** * setFirstName

* Set first name. * * @param firstName */ public void setFirstName(String firstName) { this.firstName = firstName; } /** * getLastName

* Return last name. * * @return lastName */ public String getLastName() { return lastName; } /** * setLastName

* Set last name. * * @param lastName */ public void setLastName(String lastName) { this.lastName = lastName; } /** * getLastLogin

* Return last login timestamp. * * @return lastLogin */ public Date getLastLogin() { return lastLogin; } /** * setLastLogin

* Set last login timestamp. * * @param lastLogin */ public void setLastLogin(Date lastLogin) { this.lastLogin = lastLogin; } /** * getModificationTimestamp

* Return modification timestamp. * * @return modificationTimestamp */ public Date getModificationTimestamp() { return modificationTimestamp; } /** * setModificationTimestamp

* Set modification timestamp. * * @param modificationTimestamp */ public void setModificationTimestamp(Date modificationTimestamp) { this.modificationTimestamp = modificationTimestamp; } /** * getCreationTimestamp

* Return creation timestamp. * * @return creationTimestamp */ public Date getCreationTimestamp() { return creationTimestamp; } /** * setCreationTimestamp

* Set creation timestamp. * * @param creationTimestamp */ public void setCreationTimestamp(Date creationTimestamp) { this.creationTimestamp = creationTimestamp; } /** * getLang

* Return preferred language. * * @return lang */ public String getLang() { return lang; } /** * setLang

* Set preferred language. * * @param lang */ public void setLang(String lang) { this.lang = lang; } /** * getEmail

* Return email address. * * @return email */ public String getEmail() { return email; } /** * setEmail

* Set email address. * * @param email */ public void setEmail(String email) { this.email = email; } /** * getOrganizations

* Return organizations (entity set). * * @return organizations */ public Set getOrganizations() { return organizations; } /** * setOrganizations

* Set organizations (entity set). * * @param organizations */ public void setOrganizations(Set organizations) { this.organizations = organizations; } /** * getApplications

* Return applications (entity set). * * @return applications */ public Set getApplications() { return applications; } /** * setApplications

* Set applications (entity set). * * @param applications */ public void setApplications(Set applications) { this.applications = applications; } // -------- JSON helpers for related IDs -------- /** * setOrgsIds

* Replace organizations from a list of org IDs. * * @param orgsIds */ @JsonProperty("organizations_ids") public void setOrgsIds(List orgsIds) { organizations = new HashSet<>(); for (Integer orgid : orgsIds) { Organization o = new Organization(); o.setId(orgid); organizations.add(o); } } /** * getOrgsIds

* Expose organization IDs. * * @return orgsIds */ @JsonProperty("organizations_ids") public Set getOrgsIds() { if (organizations == null) return null; Set ids = new HashSet<>(); for (Organization org : organizations) ids.add(org.getId()); return ids; } /** * setAppsIds

* Replace applications from a collection of app IDs. * * @param appIds */ @JsonProperty("applications_ids") public void setAppsIds(Collection appIds) { applications = new HashSet<>(); for (Integer appid : appIds) { Application a = new Application(); a.setId(appid); applications.add(a); } } /** * getAppsIds

* Expose application IDs. * * @return appsIds */ @JsonProperty("applications_ids") public Set getAppsIds() { if (applications == null) return null; Set ids = new HashSet<>(); for (Application app : applications) ids.add(app.getId()); return ids; } // -------- Derived scopes -------- /** * getAllOrgsIds

* Compute full organization scope including descendants. * * @return set of org IDs (may be null when no organizations) */ @JsonIgnore public Set getAllOrgsIds() { if (organizations == null) return null; Set ids = new HashSet<>(); includeAllOrgs(this.organizations, ids); return ids; } /** * getAllAppsIds

* Compute application scope (direct associations only). * * @return set of application IDs (may be null when no applications) */ @JsonIgnore public Set getAllAppsIds() { if (applications == null) return null; return this.applications.parallelStream().map(Application::getId).collect(Collectors.toSet()); } /** * includeAllOrgs

* Walk organization hierarchy to include all descendants. * * @param list current level orgs * @param orgIds accumulator of ids */ private void includeAllOrgs(Set list, Set orgIds) { for (Organization org : list) { orgIds.add(org.getId()); includeAllOrgs(org.getChildOrganizations(), orgIds); } } /** * toString

* Get the string describing the current object * * @return object string */ @Override public String toString() { return "{User: " + username + " Name: " + firstName + " " + lastName + ", last login: " + lastLogin + "}"; } /** * Rol *

* Bitmask constants for user roles. Each constant must occupy a distinct bit. */ public static class Rol { public static final int ADVANCE = 0x01; public static final int ADMIN = 0x02; public static final int BASIC = 0x04; public static final int API_CLIENT= 0x80; public static final int[] ALL = new int[] { ADVANCE, ADMIN, BASIC, API_CLIENT }; } }