Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
pl.edu.icm.unity.engine.forms.enquiry.EnquiryManagementImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2016 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.engine.forms.enquiry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import pl.edu.icm.unity.MessageSource;
import pl.edu.icm.unity.base.capacityLimit.CapacityLimitName;
import pl.edu.icm.unity.base.msgtemplates.reg.AcceptRegistrationTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.EnquiryFilledTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.NewEnquiryTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.RejectRegistrationTemplateDef;
import pl.edu.icm.unity.engine.api.EnquiryManagement;
import pl.edu.icm.unity.engine.api.EntityManagement;
import pl.edu.icm.unity.engine.api.authn.InvocationContext;
import pl.edu.icm.unity.engine.api.authn.LoginSession;
import pl.edu.icm.unity.engine.api.bulk.BulkGroupQueryService;
import pl.edu.icm.unity.engine.api.bulk.EntityInGroupData;
import pl.edu.icm.unity.engine.api.bulk.GroupMembershipData;
import pl.edu.icm.unity.engine.api.bulk.GroupStructuralData;
import pl.edu.icm.unity.engine.api.enquiry.EnquirySelector;
import pl.edu.icm.unity.engine.api.identity.EntityResolver;
import pl.edu.icm.unity.engine.api.notification.NotificationProducer;
import pl.edu.icm.unity.engine.api.registration.FormAutomationSupport;
import pl.edu.icm.unity.engine.api.registration.GroupPatternMatcher;
import pl.edu.icm.unity.engine.api.registration.PublicRegistrationURLSupport;
import pl.edu.icm.unity.engine.attribute.AttributesHelper;
import pl.edu.icm.unity.engine.authz.AuthzCapability;
import pl.edu.icm.unity.engine.authz.InternalAuthorizationManager;
import pl.edu.icm.unity.engine.capacityLimits.InternalCapacityLimitVerificator;
import pl.edu.icm.unity.engine.events.InvocationEventProducer;
import pl.edu.icm.unity.engine.forms.BaseFormValidator;
import pl.edu.icm.unity.engine.forms.InvitationPrefillInfo;
import pl.edu.icm.unity.engine.forms.RegistrationConfirmationSupport;
import pl.edu.icm.unity.engine.forms.RegistrationConfirmationSupport.Phase;
import pl.edu.icm.unity.exceptions.EngineException;
import pl.edu.icm.unity.exceptions.IllegalIdentityValueException;
import pl.edu.icm.unity.exceptions.WrongArgumentException;
import pl.edu.icm.unity.stdext.attr.StringAttribute;
import pl.edu.icm.unity.store.api.generic.EnquiryFormDB;
import pl.edu.icm.unity.store.api.generic.EnquiryResponseDB;
import pl.edu.icm.unity.store.api.tx.Transactional;
import pl.edu.icm.unity.store.api.tx.TransactionalRunner;
import pl.edu.icm.unity.types.basic.Attribute;
import pl.edu.icm.unity.types.basic.AttributeExt;
import pl.edu.icm.unity.types.basic.Entity;
import pl.edu.icm.unity.types.basic.EntityParam;
import pl.edu.icm.unity.types.basic.GroupContents;
import pl.edu.icm.unity.types.registration.AdminComment;
import pl.edu.icm.unity.types.registration.EnquiryForm;
import pl.edu.icm.unity.types.registration.EnquiryForm.EnquiryType;
import pl.edu.icm.unity.types.registration.EnquiryFormNotifications;
import pl.edu.icm.unity.types.registration.EnquiryResponse;
import pl.edu.icm.unity.types.registration.EnquiryResponseState;
import pl.edu.icm.unity.types.registration.RegistrationContext;
import pl.edu.icm.unity.types.registration.RegistrationRequestAction;
import pl.edu.icm.unity.types.registration.RegistrationRequestStatus;
import pl.edu.icm.unity.types.registration.invite.InvitationParam.InvitationType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Implementation of the enquiry management API.
*
* @author K. Benedyczak
*/
@Component
@Primary
@InvocationEventProducer
public class EnquiryManagementImpl implements EnquiryManagement
{
private final EnquiryFormDB enquiryFormDB;
private final EnquiryResponseDB requestDB;
private final NotificationProducer notificationProducer;
private final RegistrationConfirmationSupport confirmationsSupport;
private final MessageSource msg;
private final InternalAuthorizationManager authz;
private final BaseFormValidator baseFormValidator;
private final EnquiryResponsePreprocessor enquiryResponseValidator;
private final PublicRegistrationURLSupport publicRegistrationURLSupport;
private final TransactionalRunner tx;
private final SharedEnquiryManagment internalManagment;
private final EntityResolver identitiesResolver;
private final AttributesHelper dbAttributes;
private final BulkGroupQueryService bulkService;
private final InternalCapacityLimitVerificator capacityLimitVerificator;
private final EntityManagement entityManagement;
@Autowired
public EnquiryManagementImpl(EnquiryFormDB enquiryFormDB, EnquiryResponseDB requestDB,
NotificationProducer notificationProducer, RegistrationConfirmationSupport confirmationsSupport,
MessageSource msg, InternalAuthorizationManager authz, BaseFormValidator baseFormValidator,
EnquiryResponsePreprocessor enquiryResponseValidator,
PublicRegistrationURLSupport publicRegistrationURLSupport, TransactionalRunner tx,
SharedEnquiryManagment internalManagment, EntityResolver identitiesResolver, AttributesHelper dbAttributes,
@Qualifier("insecure") BulkGroupQueryService bulkService,
InternalCapacityLimitVerificator capacityLimitVerificator,
@Qualifier("insecure") EntityManagement entityManagement)
{
this.enquiryFormDB = enquiryFormDB;
this.requestDB = requestDB;
this.notificationProducer = notificationProducer;
this.confirmationsSupport = confirmationsSupport;
this.msg = msg;
this.authz = authz;
this.baseFormValidator = baseFormValidator;
this.enquiryResponseValidator = enquiryResponseValidator;
this.publicRegistrationURLSupport = publicRegistrationURLSupport;
this.tx = tx;
this.internalManagment = internalManagment;
this.identitiesResolver = identitiesResolver;
this.dbAttributes = dbAttributes;
this.bulkService = bulkService;
this.capacityLimitVerificator = capacityLimitVerificator;
this.entityManagement = entityManagement;
}
@Transactional
@Override
public void addEnquiry(EnquiryForm form) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
capacityLimitVerificator.assertInSystemLimitForSingleAdd(CapacityLimitName.EnquiryFormsCount,
() -> enquiryFormDB.getCount());
validateFormContents(form);
enquiryFormDB.create(form);
}
@Transactional
@Override
public void sendEnquiry(String enquiryId) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
EnquiryForm form = enquiryFormDB.get(enquiryId);
EnquiryFormNotifications notificationsCfg = form.getNotificationsConfiguration();
if (notificationsCfg.getEnquiryToFillTemplate() != null)
{
Map params = new HashMap<>();
params.put(NewEnquiryTemplateDef.FORM_NAME, form.getDisplayedName()
.getDefaultLocaleValue(msg));
params.put(NewEnquiryTemplateDef.URL, publicRegistrationURLSupport.getWellknownEnquiryLink(enquiryId));
GroupMembershipData bulkMembershipData = bulkService.getBulkMembershipData("/");
Map membershipInfo = bulkService.getMembershipInfo(bulkMembershipData);
for (EntityInGroupData info : membershipInfo.values())
{
if (info.relevantEnquiryForms.contains(form.getName()))
{
notificationProducer.sendNotification(new EntityParam(info.entity.getId()),
notificationsCfg.getEnquiryToFillTemplate(), params, msg.getDefaultLocaleCode(), null,
false);
}
}
}
}
@Transactional
@Override
public void removeEnquiry(String formId, boolean dropRequests) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
internalManagment.dropOrValidateFormRequests(formId, dropRequests);
enquiryFormDB.delete(formId);
}
@Transactional
@Override
public void removeEnquiryWithoutDependencyChecking(String formId) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
internalManagment.dropOrValidateFormRequests(formId, true);
enquiryFormDB.deleteWithoutDependencyChecking(formId);
}
@Transactional
@Override
public void updateEnquiry(EnquiryForm updatedForm, boolean ignoreRequestsAndInvitations) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
validateFormContents(updatedForm);
String formId = updatedForm.getName();
if (!ignoreRequestsAndInvitations)
{
internalManagment.validateIfHasPendingRequests(formId);
internalManagment.validateIfHasInvitations(updatedForm, InvitationType.ENQUIRY);
}
enquiryFormDB.update(updatedForm);
}
@Transactional
@Override
public List getEnquires() throws EngineException
{
authz.checkAuthorization(AuthzCapability.readInfo);
return enquiryFormDB.getAll();
}
@Override
public String submitEnquiryResponse(EnquiryResponse response, RegistrationContext context) throws EngineException
{
EnquiryResponseState responseFull = new EnquiryResponseState();
responseFull.setStatus(RegistrationRequestStatus.pending);
responseFull.setRequest(response);
responseFull.setRequestId(UUID.randomUUID()
.toString());
responseFull.setTimestamp(new Date());
responseFull.setRegistrationContext(context);
responseFull.setEntityId(getEntity(response.getFormId(), response.getRegistrationCode()));
FormWithInvitation formWithInvitation = recordRequestAndReturnForm(responseFull);
sendNotificationOnNewResponse(formWithInvitation.form);
internalManagment.sendInvitationProcessedNotificationIfNeeded(formWithInvitation.form,
formWithInvitation.invitation, responseFull);
boolean accepted = tryAutoProcess(formWithInvitation.form, responseFull);
Long entityId = accepted ? responseFull.getEntityId() : null;
tx.runInTransactionThrowing(() ->
{
confirmationsSupport.sendAttributeConfirmationRequest(responseFull, formWithInvitation.form, entityId,
Phase.ON_SUBMIT);
confirmationsSupport.sendIdentityConfirmationRequest(responseFull, formWithInvitation.form, entityId,
Phase.ON_SUBMIT);
});
return responseFull.getRequestId();
}
private long getEntity(String formId, String code) throws EngineException
{
if (code == null)
{
return getLoggedEntity();
} else
{
return getEntityFromInvitation(formId, code);
}
}
private Long getLoggedEntity()
{
Long entityId = null;
try
{
entityId = InvocationContext.getCurrent()
.getLoginSession()
.getEntityId();
} catch (Exception e)
{
throw new IllegalStateException("Can not get currently logged user");
}
return entityId;
}
private Long getEntityFromInvitation(String formId, String code) throws EngineException
{
return tx.runInTransactionRetThrowing(() ->
{
return enquiryResponseValidator.getEntityFromInvitationAndValidateCode(formId, code);
});
}
@Override
@Transactional
public void processEnquiryResponse(String id, EnquiryResponse finalRequest, RegistrationRequestAction action,
String publicCommentStr, String internalCommentStr) throws EngineException
{
authz.checkAuthorization(AuthzCapability.credentialModify, AuthzCapability.attributeModify,
AuthzCapability.identityModify, AuthzCapability.groupModify);
EnquiryResponseState currentRequest = requestDB.get(id);
LoginSession client = internalManagment.preprocessRequest(finalRequest, currentRequest, action);
AdminComment publicComment = internalManagment.preprocessComment(currentRequest, publicCommentStr, client,
true);
AdminComment internalComment = internalManagment.preprocessComment(currentRequest, internalCommentStr, client,
false);
EnquiryForm form = enquiryFormDB.get(currentRequest.getRequest()
.getFormId());
switch (action)
{
case drop:
internalManagment.dropEnquiryResponse(id);
break;
case reject:
internalManagment.rejectEnquiryResponse(form, currentRequest, publicComment, internalComment);
break;
case update:
updateResponse(form, currentRequest, publicComment, internalComment);
break;
case accept:
internalManagment.acceptEnquiryResponse(form, currentRequest, publicComment, internalComment, true);
break;
}
}
private void updateResponse(EnquiryForm form, EnquiryResponseState currentRequest, AdminComment publicComment,
AdminComment internalComment) throws EngineException
{
enquiryResponseValidator.validateSubmittedResponse(form, currentRequest, false);
requestDB.update(currentRequest);
internalManagment.sendProcessingNotification(form, form.getNotificationsConfiguration()
.getUpdatedTemplate(), currentRequest, form.getName(), publicComment, internalComment);
}
private FormWithInvitation recordRequestAndReturnForm(EnquiryResponseState responseFull) throws EngineException
{
return tx.runInTransactionRetThrowing(() ->
{
EnquiryForm form = enquiryFormDB.get(responseFull.getRequest()
.getFormId());
InvitationPrefillInfo validateSubmittedResponse = enquiryResponseValidator.validateSubmittedResponse(form,
responseFull, true);
boolean isSticky = form.getType()
.equals(EnquiryType.STICKY);
if (isSticky)
{
removeAllPendingRequestsOfForm(form.getName(), new EntityParam(responseFull.getEntityId()));
}
requestDB.create(responseFull);
if (!isSticky)
{
addToAttribute(responseFull.getEntityId(), EnquiryAttributeTypesProvider.FILLED_ENQUIRES,
form.getName());
}
return new FormWithInvitation(form, validateSubmittedResponse);
});
}
private void sendNotificationOnNewResponse(EnquiryForm form) throws EngineException
{
EnquiryFormNotifications notificationsCfg = form.getNotificationsConfiguration();
if (notificationsCfg.getSubmittedTemplate() != null && notificationsCfg.getAdminsNotificationGroup() != null)
{
Map params = new HashMap<>();
params.put(EnquiryFilledTemplateDef.FORM_NAME, form.getDisplayedName()
.getDefaultLocaleValue(msg));
LoginSession loginSession = InvocationContext.getCurrent()
.getLoginSession();
params.put(EnquiryFilledTemplateDef.USER, loginSession.getEntityLabel());
notificationProducer.sendNotificationToGroup(notificationsCfg.getAdminsNotificationGroup(),
notificationsCfg.getSubmittedTemplate(), params, msg.getDefaultLocaleCode());
}
}
private boolean tryAutoProcess(EnquiryForm form, EnquiryResponseState requestFull) throws EngineException
{
return tx.runInTransactionRetThrowing(() ->
{
return internalManagment.autoProcessEnquiry(form, requestFull,
"Automatic processing of the request " + requestFull.getRequestId() + " invoked, action: {0}");
});
}
private void validateFormContents(EnquiryForm form) throws EngineException
{
baseFormValidator.validateBaseFormContents(form);
EnquiryFormNotifications notCfg = form.getNotificationsConfiguration();
if (notCfg == null)
throw new WrongArgumentException("NotificationsConfiguration must be set in the form.");
baseFormValidator.checkTemplate(notCfg.getSubmittedTemplate(), EnquiryFilledTemplateDef.NAME, "enquiry filled");
baseFormValidator.checkTemplate(notCfg.getEnquiryToFillTemplate(), NewEnquiryTemplateDef.NAME, "new enquiry");
baseFormValidator.checkTemplate(notCfg.getAcceptedTemplate(), AcceptRegistrationTemplateDef.NAME,
"enquiry accepted");
baseFormValidator.checkTemplate(notCfg.getRejectedTemplate(), RejectRegistrationTemplateDef.NAME,
"enquiry rejected");
GroupStructuralData bulkData = bulkService.getBulkStructuralData("/");
Map groupAndSubgroups = bulkService.getGroupAndSubgroups(bulkData);
if (form.getTargetGroups() == null || form.getTargetGroups().length == 0)
throw new WrongArgumentException("Target groups must be set in the form.");
for (String targetGroup : form.getTargetGroups())
{
if (!GroupPatternMatcher.isValidPattern(targetGroup))
{
throw new IllegalArgumentException(targetGroup + " is not a valid target group: must start with '/'");
}
if (!groupAndSubgroups.keySet()
.contains(targetGroup))
{
throw new IllegalArgumentException(targetGroup + " is not a valid target group");
}
}
if (form.getType() == null)
throw new WrongArgumentException("Form type must be set.");
if (form.getType()
.equals(EnquiryType.STICKY))
{
if (!form.getIdentityParams()
.isEmpty())
{
throw new WrongArgumentException("Identity params in sticky enquiry forms must be empty");
}
if (!form.getCredentialParams()
.isEmpty())
{
throw new WrongArgumentException("Credential params in sticky enquiry forms must be empty");
}
}
}
@Transactional
@Override
public List getEnquiryResponses() throws EngineException
{
authz.checkAuthorization(AuthzCapability.read);
return requestDB.getAll();
}
@Transactional
@Override
public EnquiryResponseState getEnquiryResponse(String requestId)
{
authz.checkAuthorizationRT("/", AuthzCapability.read);
return requestDB.get(requestId);
}
@Transactional
@Override
public List getAvailableEnquires(EntityParam entityParam, EnquirySelector selector)
throws EngineException
{
Entity entity = entityManagement.getEntity(entityParam);
authz.checkAuthorization(authz.isSelf(entity.getId()), AuthzCapability.readInfo);
List filteredEnquiryForms = enquiryFormDB.getAll()
.stream()
.filter(selector.type.filter)
.filter(selector.accessMode.filter)
.collect(Collectors.toList());
if (filteredEnquiryForms.isEmpty())
return Collections.emptyList();
List relevantEnquiryForms = getApplicableEnquiries(entity, filteredEnquiryForms);
if (selector.type.equals(EnquirySelector.Type.STICKY))
{
return relevantEnquiryForms;
}
return filterIgnoredOrFilledFromRegularEnquires(entity, relevantEnquiryForms);
}
private List filterIgnoredOrFilledFromRegularEnquires(Entity entity,
List userApplicableEnquiries) throws EngineException
{
Set ignoredOrFilledEqnuiries = getEnquiresFromAttribute(entity.getId(),
EnquiryAttributeTypesProvider.FILLED_ENQUIRES);
ignoredOrFilledEqnuiries
.addAll(getEnquiresFromAttribute(entity.getId(), EnquiryAttributeTypesProvider.IGNORED_ENQUIRES));
return userApplicableEnquiries.stream()
.filter(f -> f.getType()
.equals(EnquiryType.STICKY) || !ignoredOrFilledEqnuiries.contains(f.getName()))
.collect(Collectors.toList());
}
private List getApplicableEnquiries(Entity entity, List allForms) throws EngineException
{
List forms = new ArrayList<>();
Set entityGroups = entityManagement.getGroups(new EntityParam(entity.getId()))
.keySet();
Collection entityAttributes = dbAttributes.getAllAttributesAsMapOneGroup(entity.getId(), "/")
.values();
for (EnquiryForm enqForm : allForms)
{
if (EnquiryTargetCondEvaluator.evaluateTargetCondition(enqForm, entity.getIdentities(), entity.getState()
.toString(), entity.getCredentialInfo(), entityGroups, entityAttributes))
forms.add(enqForm);
}
return forms;
}
private Set getEnquiresFromAttribute(long entityId, String attributeName) throws EngineException
{
Set ret = new LinkedHashSet<>();
Map attrs = dbAttributes.getAllAttributesAsMapOneGroup(entityId, "/");
if (!attrs.containsKey(attributeName))
return ret;
AttributeExt attr = attrs.get(attributeName);
attr.getValues()
.forEach(v -> ret.add(v.toString()));
return ret;
}
private void addToAttribute(long entityId, String attributeName, String value) throws EngineException
{
Set currentValues = getEnquiresFromAttribute(entityId, attributeName);
currentValues.add(value);
Attribute attribute = StringAttribute.of(attributeName, "/", new ArrayList<>(currentValues));
dbAttributes.addAttribute(entityId, attribute, true, false);
}
private void removeAllPendingRequestsOfForm(String enquiryId, EntityParam entity)
throws IllegalIdentityValueException
{
for (EnquiryResponseState en : requestDB.getAll())
{
if (!en.getStatus()
.equals(RegistrationRequestStatus.pending))
continue;
EnquiryResponse res = en.getRequest();
long entityId = identitiesResolver.getEntityId(entity);
if (res.getFormId()
.equals(enquiryId) && en.getEntityId() == entityId)
{
requestDB.delete(en.getRequestId());
}
}
}
@Transactional
@Override
public void ignoreEnquiry(String enquiryId, EntityParam entity) throws EngineException
{
long entityId = identitiesResolver.getEntityId(entity);
authz.checkAuthorization(authz.isSelf(entityId), AuthzCapability.read);
EnquiryForm form = enquiryFormDB.get(enquiryId);
if (form.getType()
.equals(EnquiryType.STICKY))
{
removeAllPendingRequestsOfForm(enquiryId, entity);
} else
{
if (form.getType() == EnquiryType.REQUESTED_MANDATORY)
throw new WrongArgumentException("The mandatory enquiry can not be marked as ignored");
addToAttribute(entityId, EnquiryAttributeTypesProvider.IGNORED_ENQUIRES, enquiryId);
}
}
@Transactional
@Override
public FormAutomationSupport getFormAutomationSupport(EnquiryForm form)
{
return confirmationsSupport.getEnquiryFormAutomationSupport(form);
}
@Transactional
@Override
public void removePendingStickyRequest(String form, EntityParam entity) throws EngineException
{
long entityId = identitiesResolver.getEntityId(entity);
authz.checkAuthorization(authz.isSelf(entityId), AuthzCapability.read);
EnquiryForm eform = enquiryFormDB.get(form);
if (!eform.getType()
.equals(EnquiryType.STICKY))
throw new WrongArgumentException("Only sticky enquiry request can be removed");
removeAllPendingRequestsOfForm(form, entity);
}
@Transactional
@Override
public EnquiryForm getEnquiry(String id) throws EngineException
{
return enquiryFormDB.get(id);
}
@Override
@Transactional
public boolean hasForm(String id)
{
return enquiryFormDB.exists(id);
}
private static class FormWithInvitation
{
public final EnquiryForm form;
public final InvitationPrefillInfo invitation;
public FormWithInvitation(EnquiryForm form, InvitationPrefillInfo invitation)
{
this.form = form;
this.invitation = invitation;
}
}
}