com.confighub.core.security.Token.java Source code

Java tutorial

Introduction

Here is the source code for com.confighub.core.security.Token.java

Source

/*
 * This file is part of ConfigHub.
 *
 * ConfigHub is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ConfigHub is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ConfigHub.If not, see <http://www.gnu.org/licenses/>.
 */

package com.confighub.core.security;

import com.confighub.core.auth.Auth;
import com.confighub.core.error.ConfigException;
import com.confighub.core.error.Error;
import com.confighub.core.organization.Team;
import com.confighub.core.repository.Repository;
import com.confighub.core.rules.AccessRuleWrapper;
import com.confighub.core.store.APersisted;
import com.confighub.core.store.diff.TokenDiffTracker;
import com.confighub.core.user.UserAccount;
import com.confighub.core.utils.Utils;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import org.hibernate.envers.RelationTargetAuditMode;

import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Entity
@Table(name = "token")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@NamedQueries({ @NamedQuery(name = "Token.getAll", query = "SELECT t FROM Token t WHERE repository=:repository"),
        @NamedQuery(name = "Token.getToken", query = "SELECT t FROM Token t WHERE token=:token AND repository=:repository"),
        @NamedQuery(name = "Token.byId", query = "SELECT t FROM Token t WHERE repository=:repository AND id=:id"), })
@EntityListeners({ TokenDiffTracker.class })
@Audited
@AuditTable("token_audit")
public class Token extends APersisted {
    private static final Logger log = LogManager.getLogger(Token.class);

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH, CascadeType.PERSIST })
    @JoinColumn(nullable = false)
    private Repository repository;

    @NotAudited
    @Column(length = 450)
    private String token;

    @Column(name = "name")
    private String name;

    @Column(name = "active")
    private boolean active;

    @Column(name = "expires")
    private Long expires;

    @Column(name = "forceKeyPushEnabled")
    private boolean forceKeyPushEnabled = false;

    @Column(name = "createdOn")
    private Long createdOn;

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH })
    private Set<SecurityProfile> securityProfiles;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH, CascadeType.PERSIST })
    @JoinColumn(name = "teamRulesId")
    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    private Team teamRules;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH, CascadeType.PERSIST })
    @JoinColumn(name = "managingTeamId")
    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    private Team managingTeam;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH, CascadeType.PERSIST })
    @JoinColumn(name = "userAccountId")
    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    private UserAccount user;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, name = "managedBy")
    private ManagedBy managedBy;

    public enum ManagedBy {
        User, Admins, Team, All
    }

    protected Token() {
    }

    /**
     * Create API Token
     *
     * @param repository
     * @param name
     * @param expiration
     * @param forceKeyPushEnabled
     * @param securityProfiles
     * @param teamRules
     * @param managingTeam
     * @param userAccount
     * @param managedBy
     */
    public Token(final Repository repository, final String name, final Long expiration,
            final boolean forceKeyPushEnabled, final List<SecurityProfile> securityProfiles, final String teamRules,
            final String managingTeam, final UserAccount userAccount, final ManagedBy managedBy) {
        this.name = name;
        this.expires = expiration;
        this.repository = repository;
        this.createdOn = (new Date()).getTime();
        this.forceKeyPushEnabled = forceKeyPushEnabled;
        this.active = true;
        this.managedBy = managedBy;

        if (null != securityProfiles) {
            for (SecurityProfile sp : securityProfiles) {
                this.addSecurityProfile(sp);
            }
        }

        if (null != teamRules) {
            Team team = repository.getTeam(teamRules);
            if (null == team) {
                throw new ConfigException(Error.Code.TEAM_NOT_FOUND);
            }

            this.teamRules = team;
        }

        switch (managedBy) {
        case User:
            if (null == userAccount) {
                throw new ConfigException(Error.Code.MISSING_PARAMS);
            }
            this.user = userAccount;
            this.teamRules = repository.getTeamForUser(userAccount);
            break;

        case Team:
            this.user = null;
            Team team = repository.getTeam(managingTeam);
            if (null == team) {
                throw new ConfigException(Error.Code.TEAM_NOT_FOUND);
            }
            this.managingTeam = team;
            break;

        case Admins:
        case All:
            this.user = null;
            this.managingTeam = null;
            break;
        }

        this.build();
    }

    /**
     * API Token can only be updated from this method.
     *
     * @param active
     * @param name
     * @param expiration
     * @param forceKeyPushEnabled
     * @param addSecurityProfiles
     * @param removeSecurityProfiles
     * @param teamRules
     * @param managingTeam
     * @param managedBy
     * @param newOwner
     * @param modificationAuthor
     */
    public void updateToken(final boolean active, final String name, final Long expiration,
            final boolean forceKeyPushEnabled, final List<SecurityProfile> addSecurityProfiles,
            final List<SecurityProfile> removeSecurityProfiles, final String teamRules, final String managingTeam,
            final ManagedBy managedBy, final String newOwner, final UserAccount modificationAuthor)
            throws ConfigException {
        boolean canEdit = isAllowedToEdit(modificationAuthor);

        if (null != addSecurityProfiles) {
            for (SecurityProfile sp : addSecurityProfiles) {
                this.addSecurityProfile(sp);
            }
        }

        if (null != removeSecurityProfiles) {
            for (SecurityProfile sp : removeSecurityProfiles) {
                this.remove(sp);
            }
        }

        if (canEdit) {
            this.active = active;

            this.name = name;
            this.expires = expiration;
            this.forceKeyPushEnabled = forceKeyPushEnabled;
            this.managedBy = managedBy;

            if (!Utils.isBlank(teamRules)) {
                Team team = repository.getTeam(teamRules);
                if (null == team) {
                    throw new ConfigException(Error.Code.TEAM_NOT_FOUND);
                }

                this.teamRules = team;
            } else {
                this.teamRules = null;
            }

            switch (managedBy) {
            case User:
                if (null == modificationAuthor) {
                    throw new ConfigException(Error.Code.MISSING_PARAMS);
                }

                if (modificationAuthor.getUsername().equals(newOwner)) {
                    this.user = modificationAuthor;

                    // If not an admin, a user has to have personal token assigned to their team.
                    if (!this.repository.isAdminOrOwner(modificationAuthor)) {
                        this.teamRules = repository.getTeamForUser(modificationAuthor);
                    }

                    this.managingTeam = null;
                }

                break;

            case Team:
                this.user = null;
                Team team = repository.getTeam(managingTeam);
                if (null == team) {
                    throw new ConfigException(Error.Code.TEAM_NOT_FOUND);
                }
                this.managingTeam = team;
                break;

            case Admins:
            case All:
                this.user = null;
                this.managingTeam = null;
                break;
            }
        }
    }

    public boolean isAllowedToDelete(UserAccount user) {
        if (null == user) {
            return false;
        }

        return this.repository.isAdminOrOwner(user)
                || (ManagedBy.User.equals(this.managedBy) && user.equals(this.user));
    }

    public boolean isAllowedToEdit(UserAccount user) {
        if (null == user) {
            return false;
        }

        if (this.repository.isAdminOrOwner(user)) {
            return true;
        }

        switch (this.managedBy) {
        // user is the token owner
        case User:
            return user.equals(this.user);

        // user is a member of a managing team
        case Team:
            return null != this.managingTeam && this.managingTeam.isMember(user);

        case Admins:
            return false;

        case All:
            return false;
        }

        return false;
    }

    public boolean isAllowedToViewToken(UserAccount user) {
        if (null == user) {
            return false;
        }

        switch (this.managedBy) {
        // user is the token owner
        case User:
            return user.equals(this.user);

        // user is a member of a managing team
        case Team:
            return null != this.managingTeam && this.managingTeam.isMember(user);

        case Admins:
            return this.repository.isAdminOrOwner(user);

        case All:
            return true;
        }

        return false;
    }

    @Override
    public String toString() {
        return String.format("Token[%5d]: %s, repoId: %d", this.id, this.name, this.repository.getId());
    }

    public JsonObject toJson() {
        JsonObject json = new JsonObject();

        json.addProperty("name", this.name);
        json.addProperty("expires", this.expires);
        json.addProperty("forceKeyPushEnabled", this.forceKeyPushEnabled);

        if (null != this.securityProfiles) {
            JsonArray sps = new JsonArray();
            for (SecurityProfile sp : this.securityProfiles) {
                sps.add(sp.getName());
            }
            json.add("sps", sps);
        }

        return json;
    }

    @PreUpdate
    @PrePersist
    public void enforce() throws ConfigException {
        if (Utils.isBlank(this.name)) {
            throw new ConfigException(Error.Code.BLANK_NAME);
        }

        if (!Utils.isNameValid(this.name)) {
            throw new ConfigException(Error.Code.ILLEGAL_CHARACTERS);
        }
    }

    public AccessRuleWrapper getRulesWrapper() {
        if (null != teamRules) {
            return this.repository.getRulesWrapper(this.teamRules);
        }

        return null;
    }

    public void clearSecurityProfiles() {
        this.securityProfiles = null;
    }

    /**
     * @param sp
     */
    private void addSecurityProfile(SecurityProfile sp) {
        if (null == this.securityProfiles) {
            this.securityProfiles = new HashSet<>();
        }

        this.securityProfiles.add(sp);
    }

    /**
     * @param sp
     */
    protected void remove(SecurityProfile sp) {
        if (null != this.securityProfiles) {
            this.securityProfiles.remove(sp);
        }
    }

    private void build() throws ConfigException {
        if (null == this.token) {
            this.token = generateToken();
        }
    }

    public boolean isExpired() {
        if (null == this.expires) {
            return false;
        }

        return this.expires < System.currentTimeMillis();
    }

    private String generateToken() {
        return Auth.getApiToken(this.repository);
    }

    // --------------------------------------------------------------------------------------------
    // POJO Ops
    // --------------------------------------------------------------------------------------------

    @Override
    public boolean equals(Object o) {
        if (null == o) {
            return false;
        }

        return o instanceof Token && ((Token) o).getId().equals(this.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.token);
    }

    // --------------------------------------------------------------------------------------------
    // Setters and getters
    // --------------------------------------------------------------------------------------------
    @Override
    public Long getId() {
        return id;
    }

    public Repository getRepository() {
        return repository;
    }

    public String getToken() {
        return token;
    }

    public String getName() {
        return name;
    }

    public boolean isForceKeyPushEnabled() {
        return forceKeyPushEnabled;
    }

    public boolean isActive() {
        return active;
    }

    public Long getExpires() {
        return expires;
    }

    public Long getCreatedOn() {
        return createdOn;
    }

    public Set<SecurityProfile> getSecurityProfiles() {
        return securityProfiles;
    }

    public Team getTeamRules() {
        return teamRules;
    }

    public Team getManagingTeam() {
        return managingTeam;
    }

    public ManagedBy getManagedBy() {
        return managedBy;
    }

    public UserAccount getUser() {
        return user;
    }

    @Override
    public ClassName getClassName() {
        return ClassName.Token;
    }
}