com.haulmont.cuba.core.sys.SecurityTokenManager.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.sys.SecurityTokenManager.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.haulmont.cuba.core.sys;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.entity.*;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.UuidProvider;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static com.haulmont.cuba.core.entity.BaseEntityInternalAccess.*;
import static org.apache.commons.lang.StringUtils.rightPad;
import static org.apache.commons.lang.StringUtils.substring;

@Component(SecurityTokenManager.NAME)
public class SecurityTokenManager {
    public static final String NAME = "cuba_SecurityTokenManager";

    @Inject
    protected ServerConfig config;
    @Inject
    protected Metadata metadata;

    protected static final String READ_ONLY_ATTRIBUTES_KEY = "__readonlyAttributes";
    protected static final String REQUIRED_ATTRIBUTES_KEY = "__requiredAttributes";
    protected static final String HIDDEN_ATTRIBUTES_KEY = "__hiddenAttributes";
    protected static final Set SYSTEM_ATTRIBUTE_KEYS = Sets.newHashSet(READ_ONLY_ATTRIBUTES_KEY,
            REQUIRED_ATTRIBUTES_KEY, HIDDEN_ATTRIBUTES_KEY);

    /**
     * Encrypt filtered data and write the result to the security token
     */
    public void writeSecurityToken(Entity entity) {
        SecurityState securityState = getOrCreateSecurityState(entity);
        if (securityState != null) {
            JSONObject jsonObject = new JSONObject();
            Multimap<String, Object> filtered = getFilteredData(securityState);
            if (filtered != null) {
                Set<Map.Entry<String, Collection<Object>>> entries = filtered.asMap().entrySet();
                String[] filteredAttributes = new String[entries.size()];
                int i = 0;
                for (Map.Entry<String, Collection<Object>> entry : entries) {
                    MetaProperty metaProperty = entity.getMetaClass().getPropertyNN(entry.getKey());
                    if (metadata.getTools().isOwningSide(metaProperty)) {
                        jsonObject.put(entry.getKey(), entry.getValue());
                    }
                    filteredAttributes[i++] = entry.getKey();
                }
                setFilteredAttributes(securityState, filteredAttributes);
            }
            if (!securityState.getReadonlyAttributes().isEmpty()) {
                jsonObject.put(READ_ONLY_ATTRIBUTES_KEY, securityState.getReadonlyAttributes());
            }
            if (!securityState.getHiddenAttributes().isEmpty()) {
                jsonObject.put(HIDDEN_ATTRIBUTES_KEY, securityState.getHiddenAttributes());
            }
            if (!securityState.getRequiredAttributes().isEmpty()) {
                jsonObject.put(REQUIRED_ATTRIBUTES_KEY, securityState.getRequiredAttributes());
            }

            String json = jsonObject.toString();
            byte[] encrypted;
            Cipher cipher = getCipher(Cipher.ENCRYPT_MODE);
            try {
                encrypted = cipher.doFinal(json.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("An error occurred while generating security token", e);
            }
            setSecurityToken(securityState, encrypted);
        }
    }

    /**
     * Decrypt security token and read filtered data
     */
    public void readSecurityToken(Entity entity) {
        SecurityState securityState = getSecurityState(entity);
        if (getSecurityToken(entity) == null) {
            return;
        }
        Multimap<String, Object> filteredData = ArrayListMultimap.create();
        BaseEntityInternalAccess.setFilteredData(securityState, filteredData);
        Cipher cipher = getCipher(Cipher.DECRYPT_MODE);
        try {
            byte[] decrypted = cipher.doFinal(getSecurityToken(securityState));
            String json = new String(decrypted, StandardCharsets.UTF_8);
            JSONObject jsonObject = new JSONObject(json);
            for (Object key : jsonObject.keySet()) {
                if (!SYSTEM_ATTRIBUTE_KEYS.contains(key)) {
                    String elementName = String.valueOf(key);
                    JSONArray jsonArray = jsonObject.getJSONArray(elementName);
                    MetaProperty metaProperty = entity.getMetaClass().getPropertyNN(elementName);
                    for (int i = 0; i < jsonArray.length(); i++) {
                        Object id = jsonArray.get(i);
                        filteredData.put(elementName, convertId(id, metaProperty));
                    }
                }
            }
            if (jsonObject.has(READ_ONLY_ATTRIBUTES_KEY)) {
                BaseEntityInternalAccess.setReadonlyAttributes(securityState,
                        parseJsonArrayAsStrings(jsonObject.getJSONArray(READ_ONLY_ATTRIBUTES_KEY)));
            }
            if (jsonObject.has(HIDDEN_ATTRIBUTES_KEY)) {
                BaseEntityInternalAccess.setHiddenAttributes(securityState,
                        parseJsonArrayAsStrings(jsonObject.getJSONArray(HIDDEN_ATTRIBUTES_KEY)));
            }
            if (jsonObject.has(REQUIRED_ATTRIBUTES_KEY)) {
                BaseEntityInternalAccess.setRequiredAttributes(securityState,
                        parseJsonArrayAsStrings(jsonObject.getJSONArray(REQUIRED_ATTRIBUTES_KEY)));
            }
        } catch (Exception e) {
            throw new RuntimeException("An error occurred while reading security token", e);
        }
    }

    protected Cipher getCipher(int mode) {
        try {
            Cipher cipher = Cipher.getInstance("AES");
            byte[] encryptionKey = rightPad(substring(config.getKeyForSecurityTokenEncryption(), 0, 16), 16)
                    .getBytes(StandardCharsets.UTF_8);

            SecretKeySpec sKeySpec = new SecretKeySpec(encryptionKey, "AES");
            cipher.init(mode, sKeySpec);
            return cipher;
        } catch (Exception e) {
            throw new RuntimeException("An error occurred while initiating encryption/decryption", e);
        }
    }

    protected String[] parseJsonArrayAsStrings(JSONArray array) {
        List<String> result = new ArrayList<>();
        for (int i = 0; i < array.length(); i++) {
            result.add(array.getString(i));
        }
        return result.toArray(new String[result.size()]);
    }

    protected Object convertId(Object value, MetaProperty metaProperty) {
        MetaClass metaClass = metaProperty.getRange().asClass();
        if (HasUuid.class.isAssignableFrom(metaClass.getJavaClass())) {
            return UuidProvider.fromString((String) value);
        }
        MetaProperty primaryKey = metadata.getTools().getPrimaryKeyProperty(metaClass);

        if (primaryKey != null) {
            Class type = primaryKey.getJavaType();
            if (UUID.class.equals(type)) {
                return UuidProvider.fromString((String) value);
            } else if (Long.class.equals(type) || IdProxy.class.equals(type)) {
                return ((Integer) value).longValue();
            } else if (Integer.class.equals(type)) {
                return value;
            } else if (String.class.equals(type)) {
                return value;
            } else {
                throw new IllegalStateException(String.format("Unsupported primary key type: %s for %s",
                        type.getSimpleName(), metaClass.getName()));
            }
        } else {
            throw new IllegalStateException(String.format("Primary key not found for %s", metaClass.getName()));
        }
    }

    /**
     * INTERNAL.
     */
    public void addFiltered(BaseGenericIdEntity<?> entity, String property, Object id) {
        SecurityState securityState = getOrCreateSecurityState(entity);
        if (getFilteredData(securityState) == null) {
            setFilteredData(securityState, ArrayListMultimap.create());
        }
        getFilteredData(securityState).put(property, id);
    }

    /**
     * INTERNAL.
     */
    public void addFiltered(BaseGenericIdEntity<?> entity, String property, Collection ids) {
        SecurityState securityState = getOrCreateSecurityState(entity);
        if (getFilteredData(securityState) == null) {
            setFilteredData(securityState, ArrayListMultimap.create());
        }
        getFilteredData(securityState).putAll(property, ids);
    }
}