ManageData.java

package access.manage;

import access.api.ApplicationController;
import access.model.Application;
import access.model.Connection;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

@SuppressWarnings({"unchecked", "rawtypes"})
public class ManageData {

    private static Set<String> secrets = Set.of("secret", "originalSecret");

    private ManageData() {
    }

    public static Map<String, Object> getMetaDataFields(Map<String, Object> data) {
        return (Map<String, Object>) data.get("metaDataFields");
    }

    public static Map<String, Object> getData(Map<String, Object> provider) {
        return (Map<String, Object>) provider.get("data");
    }

    public static String getProviderName(Map<String, Object> provider) {
        Map<String, Object> metaDataFields = getMetaDataFields(getData(provider));
        return (String) metaDataFields.get("name:en");
    }

    public static String getEntityID(Map<String, Object> provider) {
        Map<String, Object> data = getData(provider);
        return (String) data.get("entityid");
    }

    public static List<String> contactPersons(Map<String, Object> provider) {
        Pattern pattern = Pattern.compile("contacts:[0-9]:contactType");
        List contactTypes = List.of("technical", "support", "administrative");
        Map<String, Object> metaDataFields = getMetaDataFields(getData(provider));
        return metaDataFields.keySet()
                .stream()
                .filter(key -> pattern.matcher(key).matches() && contactTypes.contains(metaDataFields.get(key)))
                .map(key -> (String) metaDataFields.get(key.replace("contactType", "emailAddress")))
                .toList();

    }

    public static void removeSecrets(Map<String, Object> application) {
            List<Map<String, Object>> connections = (List<Map<String, Object>>) application.getOrDefault("connections", List.of());
            connections.forEach(connection -> {
                Map<String, Object> metaData = (Map<String, Object>) connection.getOrDefault("metaData", Map.of());
                metaData.keySet().removeIf(key -> secrets.contains(key));
            });
    }

    public static void removeSecrets(Application application) {
        Set<Connection> connections = application.getConnections();
        connections.forEach(connection -> {
            HashMap<String, Object> metaData = connection.getMetaData();
            metaData.keySet().removeIf(key -> secrets.contains(key));
        });
    }

    //Case-insensitive substring match, not an exact-key allowlist: this is used only for provider data returned by
    //raw, unauthenticated Manage lookups (see PublicController), whose metaDataFields schema is externally defined
    //and extensible (e.g. "clientSecretJWT" alongside "secret"/"originalSecret") - a fixed set of exact key names
    //can never be proven complete against that, so anything that looks credential-shaped is stripped
    private static final Pattern SECRET_LIKE_KEY = Pattern.compile("secret|password|private.?key", Pattern.CASE_INSENSITIVE);

    //Returns a shallow-copied provider with secrets removed, leaving the original (possibly cached/shared) map untouched
    public static Map<String, Object> removeSecretsFromProvider(Map<String, Object> provider) {
        Map<String, Object> data = getData(provider);
        if (data == null) {
            return provider;
        }
        Map<String, Object> metaDataFields = getMetaDataFields(data);
        if (metaDataFields == null) {
            return provider;
        }
        Map<String, Object> sanitizedMetaDataFields = new HashMap<>(metaDataFields);
        sanitizedMetaDataFields.keySet().removeIf(key -> SECRET_LIKE_KEY.matcher(key).find());

        Map<String, Object> sanitizedData = new HashMap<>(data);
        sanitizedData.put("metaDataFields", sanitizedMetaDataFields);

        Map<String, Object> sanitizedProvider = new HashMap<>(provider);
        sanitizedProvider.put("data", sanitizedData);
        return sanitizedProvider;
    }


    public static boolean isEmpty(Object object) {
        return switch (object) {
            case List l -> l.isEmpty();
            case String s -> !StringUtils.hasText(s);
            case Map m -> m.isEmpty();
            case null -> true;
            default -> false;
        };
    }

}