<commit_before>/*
 * Copyright (c) 2008-2013 Haulmont. All rights reserved.
 * Use is subject to license terms, see http://www.cuba-platform.com/license for details.
 */
package com.haulmont.cuba.security.app;

import com.haulmont.bali.db.QueryRunner;
import com.haulmont.bali.db.ResultSetHandler;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.chile.core.model.MetaModel;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.app.ClusterListener;
import com.haulmont.cuba.core.app.ClusterManagerAPI;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.app.ServerInfoAPI;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.persistence.DbTypeConverter;
import com.haulmont.cuba.security.entity.Role;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.entity.UserSessionEntity;
import com.haulmont.cuba.security.global.UserSession;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.inject.Inject;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * User sessions distributed cache.
 *
 * @author krivopustov
 * @version $Id$
 */
@Component(UserSessionsAPI.NAME)
public final class UserSessions implements UserSessionsAPI, AppContext.Listener {

    public static final String NOT_RESTRICTED = "Not restricted";

    static class UserSessionInfo implements Serializable {
        private static final long serialVersionUID = -4834267718111570841L;

        final UserSession session;
        final long since;
        volatile long lastUsedTs; // set to 0 when propagating removal to cluster

        UserSessionInfo(UserSession session, long now) {
            this.session = session;
            this.since = now;
            this.lastUsedTs = now;
        }

        @Override
        public String toString() {
            return session + ", since: " + new Date(since) + ", lastUsed: " + new Date(lastUsedTs);
        }
    }

    private Logger log = LoggerFactory.getLogger(UserSessions.class);

    private Map<UUID, UserSessionInfo> cache = new ConcurrentHashMap<>();

    private volatile int expirationTimeout = 1800;

    private Metadata metadata;

    private ClusterManagerAPI clusterManager;

    private UserSession NO_USER_SESSION;

    private GlobalConfig globalConfig;

    private ServerConfig serverConfig;

    private byte[] bytes;

    private int count;

    @Inject
    private TimeSource timeSource;

    @Inject
    private UuidSource uuidSource;

    @Inject
    private ServerInfoAPI serverInfo;

    @Inject
    private Persistence persistence;

    @Inject
    private Resources resources;

    public UserSessions() {
    }

    @Inject
    public void setMetadata(Metadata metadata) {
        this.metadata = metadata;

        User noUser = metadata.create(User.class);
        noUser.setLogin("server");
        NO_USER_SESSION = new UserSession(
                UUID.fromString("a66abe96-3b9d-11e2-9db2-3860770d7eaf"), noUser,
                Collections.<Role>emptyList(), Locale.getDefault(), true) {
            @Override
            public UUID getId() {
                return AppContext.NO_USER_CONTEXT.getSessionId();
            }
        };
        AppContext.addListener(this);
    }

    @Inject
    public void setConfiguration(Configuration configuration) {
        globalConfig = configuration.getConfig(GlobalConfig.class);
        serverConfig = configuration.getConfig(ServerConfig.class);
        setExpirationTimeoutSec(serverConfig.getUserSessionExpirationTimeoutSec());
    }

    @Inject
    public void setClusterManager(ClusterManagerAPI clusterManager) {
        this.clusterManager = clusterManager;
        this.clusterManager.addListener(
                UserSessionInfo.class,
                new ClusterListener<UserSessionInfo>() {

                    @Override
                    public void receive(UserSessionInfo message) {
                        UUID id = message.session.getId();
                        if (message.lastUsedTs == 0) {
                            cache.remove(id);
                        } else {
                            UserSessionInfo usi = cache.get(id);
                            if (usi == null || usi.lastUsedTs < message.lastUsedTs) {
                                cache.put(id, message);
                            }
                        }
                    }

                    @Override
                    public byte[] getState() {
                        if (cache.isEmpty())
                            return new byte[0];

                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        try {
                            ObjectOutputStream oos = new ObjectOutputStream(bos);
                            oos.writeInt(cache.size());
                            for (UserSessionInfo usi : cache.values()) {
                                oos.writeObject(usi);
                            }
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        return bos.toByteArray();
                    }

                    @Override
                    public void setState(byte[] state) {
                        if (state == null || state.length == 0)
                            return;

                        ByteArrayInputStream bis = new ByteArrayInputStream(state);
                        try {
                            ObjectInputStream ois = new ObjectInputStream(bis);
                            int size = ois.readInt();
                            for (int i = 0; i < size; i++) {
                                UserSessionInfo usi = (UserSessionInfo) ois.readObject();
                                receive(usi);
                            }
                        } catch (IOException | ClassNotFoundException e) {
                            log.error("Error receiving state", e);
                        }
                    }
                }
        );
    }

    @Override
    public void applicationStarted() {
        String encodedStr = resources.getResourceAsString(serverConfig.getLicensePath());
        if (encodedStr == null) {
            log.error("\n======================================================"
                    + "\nInvalid license path: " + serverConfig.getLicensePath()
                    + "\n======================================================");
        } else {
            Object[] objects;
            try {
                bytes = Base64.decodeBase64(encodedStr);
            } catch (Exception ignored) {
            }
            objects = decode();
            if (objects != null) {
                StringBuilder sb = new StringBuilder();
                sb.append("\n======================================================");
                sb.append("\nCUBA platform license");
                sb.append("\nType: ").append(objects[0]);
                sb.append("\nLicense ID: ").append(objects[1]);
                sb.append("\nLicensed to: ").append(objects[2]);
                sb.append("\nApplication: ").append(objects[3]);
                sb.append("\nValid for CUBA platform releases up to: ").append(objects[4]);
                sb.append("\nMaximum number of sessions: ").append(((int) objects[5]) == 0 ? NOT_RESTRICTED : objects[5]);
                sb.append("\nMaximum number of entities: ").append(((int) objects[6]) == 0 ? NOT_RESTRICTED : objects[6]);
                sb.append("\n======================================================");
                log.warn(sb.toString());
            }
        }

        if (!globalConfig.getTestMode()) {
            Timer timer = new Timer(true);
            timer.schedule(<commit_msg>Eliminate entity creation without Metadata.create call #PL-6355<commit_after>/*
 * Copyright (c) 2008-2013 Haulmont. All rights reserved.
 * Use is subject to license terms, see http://www.cuba-platform.com/license for details.
 */
package com.haulmont.cuba.security.app;

import com.haulmont.bali.db.QueryRunner;
import com.haulmont.bali.db.ResultSetHandler;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.chile.core.model.MetaModel;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.app.ClusterListener;
import com.haulmont.cuba.core.app.ClusterManagerAPI;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.app.ServerInfoAPI;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.persistence.DbTypeConverter;
import com.haulmont.cuba.security.entity.Role;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.entity.UserSessionEntity;
import com.haulmont.cuba.security.global.UserSession;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.inject.Inject;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * User sessions distributed cache.
 *
 * @author krivopustov
 * @version $Id$
 */
@Component(UserSessionsAPI.NAME)
public final class UserSessions implements UserSessionsAPI, AppContext.Listener {

    public static final String NOT_RESTRICTED = "Not restricted";

    static class UserSessionInfo implements Serializable {
        private static final long serialVersionUID = -4834267718111570841L;

        final UserSession session;
        final long since;
        volatile long lastUsedTs; // set to 0 when propagating removal to cluster

        UserSessionInfo(UserSession session, long now) {
            this.session = session;
            this.since = now;
            this.lastUsedTs = now;
        }

        @Override
        public String toString() {
            return session + ", since: " + new Date(since) + ", lastUsed: " + new Date(lastUsedTs);
        }
    }

    private Logger log = LoggerFactory.getLogger(UserSessions.class);

    private Map<UUID, UserSessionInfo> cache = new ConcurrentHashMap<>();

    private volatile int expirationTimeout = 1800;

    private ClusterManagerAPI clusterManager;

    private UserSession NO_USER_SESSION;

    private GlobalConfig globalConfig;

    private ServerConfig serverConfig;

    private byte[] bytes;

    private int count;

    @Inject
    private TimeSource timeSource;

    @Inject
    private UuidSource uuidSource;

    @Inject
    private ServerInfoAPI serverInfo;

    @Inject
    private Persistence persistence;

    @Inject
    private Resources resources;

    @Inject
    private Metadata metadata;

    public UserSessions() {
        AppContext.addListener(this);
    }

    @Inject
    public void setConfiguration(Configuration configuration) {
        globalConfig = configuration.getConfig(GlobalConfig.class);
        serverConfig = configuration.getConfig(ServerConfig.class);
        setExpirationTimeoutSec(serverConfig.getUserSessionExpirationTimeoutSec());
    }

    @Inject
    public void setClusterManager(ClusterManagerAPI clusterManager) {
        this.clusterManager = clusterManager;
        this.clusterManager.addListener(
                UserSessionInfo.class,
                new ClusterListener<UserSessionInfo>() {

                    @Override
                    public void receive(UserSessionInfo message) {
                        UUID id = message.session.getId();
                        if (message.lastUsedTs == 0) {
                            cache.remove(id);
                        } else {
                            UserSessionInfo usi = cache.get(id);
                            if (usi == null || usi.lastUsedTs < message.lastUsedTs) {
                                cache.put(id, message);
                            }
                        }
                    }

                    @Override
                    public byte[] getState() {
                        if (cache.isEmpty())
                            return new byte[0];

                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        try {
                            ObjectOutputStream oos = new ObjectOutputStream(bos);
                            oos.writeInt(cache.size());
                            for (UserSessionInfo usi : cache.values()) {
                                oos.writeObject(usi);
                            }
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        return bos.toByteArray();
                    }

                    @Override
                    public void setState(byte[] state) {
                        if (state == null || state.length == 0)
                            return;

                        ByteArrayInputStream bis = new ByteArrayInputStream(state);
                        try {
                            ObjectInputStream ois = new ObjectInputStream(bis);
                            int size = ois.readInt();
                            for (int i = 0; i < size; i++) {
                                UserSessionInfo usi = (UserSessionInfo) ois.readObject();
                                receive(usi);
                            }
                        } catch (IOException | ClassNotFoundException e) {
                            log.error("Error receiving state", e);
                        }
                    }
                }
        );
    }

    @Override
    public void applicationStarted() {
        User noUser = metadata.create(User.class);
        noUser.setLogin("server");
        NO_USER_SESSION = new UserSession(
                UUID.fromString("a66abe96-3b9d-11e2-9db2-3860770d7eaf"), noUser,
                Collections.<Role>emptyList(), Locale.getDefault(), true) {
            @Override
            public UUID getId() {
                return AppContext.NO_USER_CONTEXT.getSessionId();
            }
        };

        String encodedStr = resources.getResourceAsString(serverConfig.getLicensePath());
        if (encodedStr == null) {
            log.error("\n======================================================"
                    + "\nInvalid license path: " + serverConfig.getLicensePath()
                    + "\n======================================================");
        } else {
            Object[] objects;
            try {
                bytes = Base64.decodeBase64(encodedStr);
            } catch (Exception ignored) {
            }
            objects = decode();
            if (objects != null) {
                StringBuilder sb = new StringBuilder();
                sb.append("\n======================================================");
                sb.append("\nCUBA platform license");
                sb.append("\nType: ").append(objects[0]);
                sb.append("\nLicense ID: ").append(objects[1]);
                sb.append("\nLicensed to: ").append(objects[2]);
                sb.append("\nApplication: ").append(objects[3]);
                sb.append("\nValid for CUBA platform releases up to: ").append(objects[4]);
                sb.append("\nMaximum number of sessions: ").append(((int) objects[5]) == 0 ? NOT_RESTRICTED : objects[5]);
                sb.append("\nMaximum number of entities: ").append(((int) objects[6]) == 0 ? NOT_RESTRICTED : objects[6]);
                sb.append("\n======================================================");
                log.warn(sb.toString());
            }
        }

        if (!globalConfig.getTestMode()) {
            Timer timer = new Timer(true);
            timer.schedule(
{'fraction_non_alphanumeric': 0.12027329485216162, 'fraction_numerical': 0.009306160914124161, 'mean_word_length': 1.9497915218902016, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5527209', 'n_tokens_mistral': 4471, 'n_tokens_neox': 4166, 'n_words': 913}