/* * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under * the Licence. * * This product combines work with different licenses. See the "NOTICE" text file for details on the * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative * works that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.eaaf.core.impl.idp.auth.data; import java.io.Serializable; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import at.gv.egiz.eaaf.core.api.data.EaafConstants; import at.gv.egiz.eaaf.core.api.idp.EaafAuthProcessDataConstants; import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; import at.gv.egiz.eaaf.core.exceptions.EaafJsonMapperException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import at.gv.egiz.eaaf.core.impl.utils.DefaultJsonMapper; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j public class AuthProcessDataWrapper implements IAuthProcessDataContainer, EaafAuthProcessDataConstants { public static final String GENERIC_DATA_PREFIX = "$GENERIC$_"; public static final String PATTERN_ISSUE_INSTANT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; protected Map authProcessData; public AuthProcessDataWrapper(final Map authProcessData) { this.authProcessData = authProcessData; } @Override public Instant getIssueInstant() { return buildDateTimeUtcDate(wrapStoredObject(VALUE_ISSUEINSTANT, null, String.class)); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIssueInstant() */ @Override public String getIssueInstantString() { return wrapStoredObject(VALUE_ISSUEINSTANT, null, String.class); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIssueInstant( * java.lang.String) */ @Override public void setIssueInstant(final String issueInstant) { if (buildDateTimeUtcDate(issueInstant) != null) { authProcessData.put(VALUE_ISSUEINSTANT, issueInstant); } else if (StringUtils.isNotEmpty(issueInstant)) { log.warn("Can not parse IssueInstant of authentication: {}", issueInstant); } } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIssueInstant( * java.lang.String) */ @Override public void setIssueInstant(final Instant issueInstant) { authProcessData.put(VALUE_ISSUEINSTANT, buildDateTimeUtcString(issueInstant)); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isAuthenticated() */ @Override public boolean isAuthenticated() { return wrapStoredObject(FLAG_IS_AUTHENTICATED, false, Boolean.class); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setAuthenticated( * boolean) */ @Override public void setAuthenticated(final boolean authenticated) { authProcessData.put(FLAG_IS_AUTHENTICATED, authenticated); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIdentityLink() */ @Override public IIdentityLink getIdentityLink() { return wrapStoredObject(VALUE_IDENTITYLINK, null, IIdentityLink.class); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIdentityLink(at. * gv.egovernment.moa .id.auth.data.IdentityLink) */ @Override public void setIdentityLink(final IIdentityLink identityLink) { authProcessData.put(VALUE_IDENTITYLINK, identityLink); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isMandateUsed() */ @Override public boolean isMandateUsed() { return wrapStoredObject(FLAG_USE_MANDATE, false, Boolean.class); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setUseMandates( * boolean) */ @Override public void setUseMandates(final boolean useMandates) { authProcessData.put(FLAG_USE_MANDATE, useMandates); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getQAALevel() */ @Override public String getQaaLevel() { return wrapStoredObject(VALUE_QAALEVEL, null, String.class); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setQAALevel(java. * lang.String) */ @Override public void setQaaLevel(final String qaaLevel) { authProcessData.put(VALUE_QAALEVEL, qaaLevel); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isForeigner() */ @Override public boolean isForeigner() { return wrapStoredObject(FLAG_IS_FOREIGNER, false, Boolean.class); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setForeigner( * boolean) */ @Override public void setForeigner(final boolean isForeigner) { authProcessData.put(FLAG_IS_FOREIGNER, isForeigner); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isOW() */ @Override public boolean isOW() { return wrapStoredObject(FLAG_IS_ORGANWALTER, false, Boolean.class); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setOW(boolean) */ @Override public void setOW(final boolean isOW) { authProcessData.put(FLAG_IS_ORGANWALTER, isOW); } @Override public boolean isEidProcess() { return wrapStoredObject(FLAG_IS_NEW_EID_PROCESS, false, Boolean.class); } @Override public void setEidProcess(final boolean value) { authProcessData.put(FLAG_IS_NEW_EID_PROCESS, value); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getSessionCreated() */ @Override public Instant getSessionCreated() { return buildDateTimeUtcDate(wrapStoredObject(EaafConstants.AUTH_DATA_CREATED, null, String.class)); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession# * getGenericSessionDataStorage() */ @Override public Map getGenericSessionDataStorage() { return authProcessData.entrySet().stream() .filter(el -> el.getKey().startsWith(GENERIC_PREFIX)) .map(el -> { Object obj = el.getValue(); if (obj instanceof String && ((String) obj).startsWith(GENERIC_DATA_PREFIX)) { log.trace("Find generic JSON serialized data. Starting de-serialization ... "); el.setValue(objectSaveJsonDeserialization(obj)); } return el; }) .collect( Collectors.toMap( el -> el.getKey().substring(GENERIC_PREFIX.length()), value -> value.getValue())); } @Override public Stream> getGenericSessionDataStream() { return getGenericSessionDataStorage().entrySet().stream(); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession# * getGenericDataFromSession(java.lang. String) */ @Override public Object getGenericDataFromSession(final String key) { return wrapStoredObject(GENERIC_PREFIX + key, null, Object.class); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession# * getGenericDataFromSession(java.lang. String, java.lang.Class) */ @Override public T getGenericDataFromSession(final String key, final Class clazz) { return wrapStoredObject(GENERIC_PREFIX + key, null, clazz); } /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession# * setGenericDataToSession(java.lang. String, java.lang.Object) */ @Override public void setGenericDataToSession(final String key, final Object object) throws EaafStorageException { authProcessData.put(GENERIC_PREFIX + key, objectSaveJsonSerialization(object)); } protected T wrapStoredObject(final String key, final Object defaultValue, final Class clazz) { if (StringUtils.isNotEmpty(key)) { final Object obj = authProcessData.get(key); // check if it is a generic container if (obj instanceof String && ((String) obj).startsWith(GENERIC_DATA_PREFIX)) { log.trace("Find generic JSON serialized data. Starting de-serialization ... "); return (T) objectSaveJsonDeserialization(obj); // check if object is already of valid type } else if (obj != null && clazz.isInstance(obj)) { return (T) obj; // check if byte[] was requested and object is of type String in that case it's // B64 content } else if (obj instanceof String && clazz.equals(byte[].class)) { try { return (T) java.util.Base64.getDecoder().decode((String) obj); } catch (IllegalArgumentException e) { log.warn("Can not decode session-object with key: {}", key, e); } } else if (obj != null) { log.warn("Can not unwrap data object with key: {} because requires class: {} but has: {}", key, clazz.getName(), obj.getClass().getName()); } } // return default value if we get not other result if (defaultValue == null) { return null; } else if (clazz.isInstance(defaultValue)) { return (T) defaultValue; } else { log.error("DefaultValue: " + defaultValue.getClass().getName() + " is not of Type:" + clazz.getName()); throw new IllegalStateException("DefaultValue: " + defaultValue.getClass().getName() + " is not of Type:" + clazz.getName()); } } /** * Builds a {@link String} dateTime value in UTC from a {@link Date} value. * * @param date the {@link Date} that should be transformed * @return The {@link String} representation of the date in * yyyy-MM-dd'T'HH:mm:ss'Z', or null if {@link Date} was null */ @Nullable public static String buildDateTimeUtcString(@Nullable final Instant date) { if (date == null) { return null; } return DateTimeFormatter .ofPattern(PATTERN_ISSUE_INSTANT) .withZone(ZoneId.of("UTC")) .format(date); } /** * Builds a {@link String} dateTime value in UTC from a {@link Date} value. * * @param date the {@link String} in yyyy-MM-dd'T'HH:mm:ss'Z' * format that should be transformed * @return The {@link Date} representation of the date, otherwise null * if input parameter was null or invalid */ @Nullable public static Instant buildDateTimeUtcDate(@Nullable final String date) { if (date != null) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(PATTERN_ISSUE_INSTANT); return LocalDateTime.parse(date, dateTimeFormatter).atZone(ZoneId.of("UTC")).toInstant(); } return null; } /** * Object save JSON serialization of arbitrary objects. * * @param object Element to store * @return Object for putting into cache * @throws EaafStorageException In case of a JSON serialization error */ protected final Object objectSaveJsonSerialization(Object object) throws EaafStorageException { try { if (object == null || object instanceof String || object instanceof Boolean || object instanceof Integer) { return object; } else { return GENERIC_DATA_PREFIX + DefaultJsonMapper.serialize(RawDataHolder.builder() .object(DefaultJsonMapper.serialize(object)) .clazzType(object.getClass().getName()) .build()); } } catch (EaafJsonMapperException e) { throw new EaafStorageException("Can no serialize object to JSON", e); } } /** * Object save JSON serialization of arbitrary objects. * * @param object Element to store * @return Object for putting into cache */ protected final Object failSaveObjectSaveJsonSerialization(Object object) { try { return objectSaveJsonSerialization(object); } catch (EaafStorageException e) { log.error("Can not store object: {} into session. Return null ... ", object.getClass().getName(), e); return null; } } private Object objectSaveJsonDeserialization(Object data) { try { if (data == null) { return null; } else if (data instanceof String) { RawDataHolder holder = (RawDataHolder) DefaultJsonMapper.deserialize( ((String) data).substring(GENERIC_DATA_PREFIX.length()), RawDataHolder.class); Class clz = Class.forName(holder.getClazzType()); return DefaultJsonMapper.deserialize(holder.getObject(), clz); } else { log.error("Can not deserialize: {} because it's not of type String", data.getClass().getName()); } } catch (final Exception e) { log.warn("Generic request-data object can not be casted to requested type", e); } return null; } @Data @Builder @NoArgsConstructor @AllArgsConstructor private static class RawDataHolder implements Serializable { private static final long serialVersionUID = -3827971372737399528L; private String object; private String clazzType; } }