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.reg.RegistrationsManagementImpl Maven / Gradle / Ivy
/**
* Copyright (c) 2013 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.engine.forms.reg;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
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.InvitationTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.RejectRegistrationTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.SubmitRegistrationTemplateDef;
import pl.edu.icm.unity.base.msgtemplates.reg.UpdateRegistrationTemplateDef;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.RegistrationsManagement;
import pl.edu.icm.unity.engine.api.authn.LoginSession;
import pl.edu.icm.unity.engine.api.notification.NotificationProducer;
import pl.edu.icm.unity.engine.api.registration.FormAutomationSupport;
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.credential.CredentialReqRepository;
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.store.api.generic.InvitationDB;
import pl.edu.icm.unity.store.api.generic.RegistrationFormDB;
import pl.edu.icm.unity.store.api.generic.RegistrationRequestDB;
import pl.edu.icm.unity.store.api.tx.Transactional;
import pl.edu.icm.unity.store.api.tx.TransactionalRunner;
import pl.edu.icm.unity.types.registration.AdminComment;
import pl.edu.icm.unity.types.registration.RegistrationContext;
import pl.edu.icm.unity.types.registration.RegistrationContext.TriggeringMode;
import pl.edu.icm.unity.types.registration.RegistrationForm;
import pl.edu.icm.unity.types.registration.RegistrationFormNotifications;
import pl.edu.icm.unity.types.registration.RegistrationRequest;
import pl.edu.icm.unity.types.registration.RegistrationRequestAction;
import pl.edu.icm.unity.types.registration.RegistrationRequestState;
import pl.edu.icm.unity.types.registration.RegistrationRequestStatus;
import pl.edu.icm.unity.types.registration.invite.InvitationParam.InvitationType;
import pl.edu.icm.unity.types.registration.invite.InvitationWithCode;
/**
* Implementation of registrations subsystem.
*
* @author K. Benedyczak
*/
@Component
@Primary
@InvocationEventProducer
public class RegistrationsManagementImpl implements RegistrationsManagement
{
private static final Logger log = Log.getLogger(Log.U_SERVER_FORMS, RegistrationsManagementImpl.class);
private RegistrationFormDB formsDB;
private InvitationDB invitationDB;
private RegistrationRequestDB requestDB;
private CredentialReqRepository credentialReqRepository;
private RegistrationConfirmationSupport confirmationsSupport;
private InternalAuthorizationManager authz;
private NotificationProducer notificationProducer;
private SharedRegistrationManagment internalManagment;
private MessageSource msg;
private TransactionalRunner tx;
private RegistrationRequestPreprocessor registrationRequestValidator;
private BaseFormValidator baseValidator;
private InternalCapacityLimitVerificator capacityLimitVerificator;
@Autowired
public RegistrationsManagementImpl(RegistrationFormDB formsDB, InvitationDB invitationDB,
RegistrationRequestDB requestDB, CredentialReqRepository credentialReqDB,
RegistrationConfirmationSupport confirmationsSupport, InternalAuthorizationManager authz,
NotificationProducer notificationProducer, SharedRegistrationManagment internalManagment, MessageSource msg,
TransactionalRunner tx, RegistrationRequestPreprocessor registrationRequestValidator,
BaseFormValidator baseValidator, InternalCapacityLimitVerificator capacityLimitVerificator)
{
this.formsDB = formsDB;
this.invitationDB = invitationDB;
this.requestDB = requestDB;
this.credentialReqRepository = credentialReqDB;
this.confirmationsSupport = confirmationsSupport;
this.authz = authz;
this.notificationProducer = notificationProducer;
this.internalManagment = internalManagment;
this.msg = msg;
this.tx = tx;
this.registrationRequestValidator = registrationRequestValidator;
this.baseValidator = baseValidator;
this.capacityLimitVerificator = capacityLimitVerificator;
}
@Override
@Transactional
public void addForm(RegistrationForm form) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
capacityLimitVerificator.assertInSystemLimitForSingleAdd(CapacityLimitName.RegistrationFormsCount,
() -> formsDB.getCount());
validateFormContents(form);
formsDB.create(form);
}
@Override
@Transactional
public void removeForm(String formId, boolean dropRequests) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
internalManagment.dropOrValidateFormRequests(formId, dropRequests);
formsDB.delete(formId);
}
@Override
@Transactional
public void removeFormWithoutDependencyChecking(String formId) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
internalManagment.dropOrValidateFormRequests(formId, true);
formsDB.deleteWithoutDependencyChecking(formId);
}
@Override
@Transactional
public void updateForm(RegistrationForm updatedForm, boolean ignoreRequestsAndInvitations) throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
validateFormContents(updatedForm);
String formId = updatedForm.getName();
if (!ignoreRequestsAndInvitations)
{
internalManagment.validateIfHasPendingRequests(formId);
internalManagment.validateIfHasInvitations(updatedForm, InvitationType.REGISTRATION);
}
formsDB.update(updatedForm);
}
@Override
@Transactional
public List getForms() throws EngineException
{
authz.checkAuthorization(AuthzCapability.readInfo);
return formsDB.getAll();
}
@Override
public String submitRegistrationRequest(RegistrationRequest request, final RegistrationContext context)
throws EngineException
{
authz.checkAuthorization(AuthzCapability.maintenance);
RegistrationRequestState requestFull = new RegistrationRequestState();
requestFull.setStatus(RegistrationRequestStatus.pending);
requestFull.setRequest(request);
requestFull.setRequestId(UUID.randomUUID().toString());
requestFull.setTimestamp(new Date());
requestFull.setRegistrationContext(context);
FormWithInvitation formWithInvitation = recordRequestAndReturnForm(requestFull);
sendSubmitNotificationToAdminGroup(formWithInvitation.form, requestFull);
internalManagment.sendInvitationProcessedNotificationIfNeeded(formWithInvitation.form, formWithInvitation.invitation, requestFull);
Long entityId = tryAutoProcess(formWithInvitation.form, requestFull, context);
tx.runInTransactionThrowing(() ->
{
confirmationsSupport.sendAttributeConfirmationRequest(requestFull, entityId, formWithInvitation.form, Phase.ON_SUBMIT);
confirmationsSupport.sendIdentityConfirmationRequest(requestFull, entityId, formWithInvitation.form, Phase.ON_SUBMIT);
});
return requestFull.getRequestId();
}
private FormWithInvitation recordRequestAndReturnForm(RegistrationRequestState requestFull) throws EngineException
{
return tx.runInTransactionRetThrowing(() ->
{
RegistrationRequest request = requestFull.getRequest();
RegistrationForm form = formsDB.get(request.getFormId());
InvitationPrefillInfo invitationPrefillInfo;
if (isCredentialsValidationSkipped(requestFull.getRegistrationContext().triggeringMode))
invitationPrefillInfo = registrationRequestValidator.validateSubmittedRequestExceptCredentials(form, request, true);
else
invitationPrefillInfo = registrationRequestValidator.validateSubmittedRequest(form, request, true);
requestDB.create(requestFull);
return new FormWithInvitation(form, invitationPrefillInfo);
});
}
/**
* When user enters the registration form after selecting an option of remote
* authentication to fill out a form, the credentials are filtered out by
* default and not available to the user. This behavior is fixed, meaning no
* configuration option to control this.
*/
private boolean isCredentialsValidationSkipped(TriggeringMode mode)
{
return mode == TriggeringMode.afterRemoteLoginFromRegistrationForm;
}
private void sendSubmitNotificationToAdminGroup(RegistrationForm form, RegistrationRequestState requestFull)
throws EngineException
{
RegistrationFormNotifications notificationsCfg = form.getNotificationsConfiguration();
if (notificationsCfg.getSubmittedTemplate() != null && notificationsCfg.getAdminsNotificationGroup() != null)
{
Map params = internalManagment.getBaseNotificationParams(form.getName(),
requestFull.getRequestId());
notificationProducer.sendNotificationToGroup(notificationsCfg.getAdminsNotificationGroup(),
notificationsCfg.getSubmittedTemplate(), params, msg.getDefaultLocaleCode());
}
}
private Long tryAutoProcess(RegistrationForm form, RegistrationRequestState requestFull,
RegistrationContext context) throws EngineException
{
try
{
return tx.runInTransactionRetThrowing(() ->
{
return internalManagment.autoProcess(form, requestFull, "Automatic processing of the request "
+ requestFull.getRequestId() + " of form " + form.getName() + " invoked, action: {0}");
});
} catch (Exception e)
{
log.warn("Auto processing of a request failed", e);
recordAutoAcceptFailureInRequestComment(requestFull, e);
throw e;
}
}
private void recordAutoAcceptFailureInRequestComment(RegistrationRequestState requestFull, Exception e)
{
try
{
tx.runInTransaction(() ->
{
RegistrationRequestState unprocessedRequest = requestDB.get(requestFull.getRequestId());
unprocessedRequest.getAdminComments()
.add(new AdminComment("Automatic request processing failed: " + e.getMessage(), 0, false));
requestDB.update(unprocessedRequest);
});
} catch (Exception e2)
{
log.error("Can not record failure of auto-processing in requests messages", e2);
}
}
@Override
@Transactional
public List getRegistrationRequests() throws EngineException
{
authz.checkAuthorization(AuthzCapability.read);
return requestDB.getAll();
}
@Override
@Transactional
public RegistrationRequestState getRegistrationRequest(String id) throws EngineException
{
authz.checkAuthorization(AuthzCapability.read);
return requestDB.get(id);
}
@Override
@Transactional
public boolean hasForm(String id)
{
authz.checkAuthorizationRT("/", AuthzCapability.read);
return formsDB.exists(id);
}
@Override
@Transactional
public void processRegistrationRequest(String id, RegistrationRequest finalRequest,
RegistrationRequestAction action, String publicCommentStr, String internalCommentStr) throws EngineException
{
authz.checkAuthorization(AuthzCapability.credentialModify, AuthzCapability.attributeModify,
AuthzCapability.identityModify, AuthzCapability.groupModify);
RegistrationRequestState 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);
RegistrationForm form = formsDB.get(currentRequest.getRequest().getFormId());
switch (action)
{
case drop:
internalManagment.dropRequest(id);
break;
case reject:
internalManagment.rejectRequest(form, currentRequest, publicComment, internalComment);
break;
case update:
updateRequest(form, currentRequest, publicComment, internalComment);
break;
case accept:
internalManagment.acceptRequest(form, currentRequest, publicComment, internalComment, true);
break;
}
}
private void updateRequest(RegistrationForm form, RegistrationRequestState currentRequest,
AdminComment publicComment, AdminComment internalComment) throws EngineException
{
registrationRequestValidator.validateSubmittedRequest(form, currentRequest.getRequest(), false);
requestDB.update(currentRequest);
RegistrationFormNotifications notificationsCfg = form.getNotificationsConfiguration();
internalManagment.sendProcessingNotification(notificationsCfg.getUpdatedTemplate(), currentRequest,
form.getName(), false, publicComment, internalComment, notificationsCfg);
}
private void validateFormContents(RegistrationForm form) throws EngineException
{
baseValidator.validateBaseFormContents(form);
if (form.isByInvitationOnly())
{
if (!form.isPubliclyAvailable())
throw new IllegalArgumentException("Registration form which " + "is by invitation only must be public");
if (form.getRegistrationCode() != null)
throw new IllegalArgumentException(
"Registration form which " + "is by invitation only must not have a static registration code");
}
if (form.getDefaultCredentialRequirement() == null)
throw new IllegalArgumentException("Credential requirement must be set for the form");
if (credentialReqRepository.get(form.getDefaultCredentialRequirement()) == null)
throw new IllegalArgumentException(
"Credential requirement " + form.getDefaultCredentialRequirement() + " does not exist");
RegistrationFormNotifications notCfg = form.getNotificationsConfiguration();
if (notCfg == null)
throw new IllegalArgumentException("NotificationsConfiguration must be set in the form.");
baseValidator.checkTemplate(notCfg.getAcceptedTemplate(), AcceptRegistrationTemplateDef.NAME,
"accepted registration request");
baseValidator.checkTemplate(notCfg.getRejectedTemplate(), RejectRegistrationTemplateDef.NAME,
"rejected registration request");
baseValidator.checkTemplate(notCfg.getSubmittedTemplate(), SubmitRegistrationTemplateDef.NAME,
"submitted registration request");
baseValidator.checkTemplate(notCfg.getUpdatedTemplate(), UpdateRegistrationTemplateDef.NAME,
"updated registration request");
baseValidator.checkTemplate(notCfg.getInvitationTemplate(), InvitationTemplateDef.NAME, "invitation");
if (form.getCaptchaLength() > RegistrationForm.MAX_CAPTCHA_LENGTH)
throw new IllegalArgumentException(
"Captcha can not be longer then " + RegistrationForm.MAX_CAPTCHA_LENGTH + " characters");
if (form.getTitle2ndStage() != null)
{
baseValidator.validateFreemarkerTemplate("Second stage title", form.getTitle2ndStage());
}
}
@Override
@Transactional
public FormAutomationSupport getFormAutomationSupport(RegistrationForm form)
{
return confirmationsSupport.getRegistrationFormAutomationSupport(form);
}
@Override
@Transactional
public RegistrationForm getForm(String id) throws EngineException
{
return formsDB.get(id);
}
public InvitationWithCode getInvitation(String code) throws EngineException
{
return tx.runInTransactionRet(() -> invitationDB.get(code));
}
private static class FormWithInvitation
{
public final RegistrationForm form;
public final InvitationPrefillInfo invitation;
public FormWithInvitation(RegistrationForm form, InvitationPrefillInfo invitation)
{
this.form = form;
this.invitation = invitation;
}
}
}