use of eu.bcvsolutions.idm.core.api.event.EventContext in project CzechIdMng by bcvsolutions.
the class DefaultProvisioningExecutor method executeInternal.
/**
* We need to wait to transaction commit, when provisioning is executed - all accounts have to be prepared.
* Next processing is executed outside a transaction
* => operation states has to be saved in new transactions
* => rollback on the target system is not possible anyway
*
* @param provisioningOperation
* @return
*/
@TransactionalEventListener
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public SysProvisioningOperationDto executeInternal(SysProvisioningOperationDto provisioningOperation) {
Assert.notNull(provisioningOperation, "Provisioning operation is required.");
Assert.notNull(provisioningOperation.getSystemEntity(), "System entity is required.");
Assert.notNull(provisioningOperation.getProvisioningContext(), "Provisioning context is required.");
//
try {
boolean checkNotExecuted = provisioningOperation.isSynchronousProvisioning();
if (provisioningOperationService.isNew(provisioningOperation)) {
provisioningOperation = persistOperation(provisioningOperation);
checkNotExecuted = true;
}
if (checkNotExecuted && provisioningOperation.getResult() != null && OperationState.NOT_EXECUTED == provisioningOperation.getResult().getState()) {
return provisioningOperation;
}
//
CoreEvent<SysProvisioningOperationDto> event = new CoreEvent<SysProvisioningOperationDto>(provisioningOperation.getOperationType(), provisioningOperation);
try {
// set a global provisioning timeout even for synchronous call
FutureTask<EventContext<SysProvisioningOperationDto>> futureTask = new FutureTask<EventContext<SysProvisioningOperationDto>>(new Callable<EventContext<SysProvisioningOperationDto>>() {
@Override
public EventContext<SysProvisioningOperationDto> call() {
return entityEventManager.process(event);
}
});
// thread pool is not used here
Thread thread = new Thread(new DelegatingSecurityContextRunnable(new DelegatingTransactionContextRunnable(futureTask)));
thread.start();
//
// global timeout by configuration
long timeout = provisioningConfiguration.getTimeout();
try {
// TODO: non blocking wait if possible (refactoring is needed + java 9 helps)
EventContext<SysProvisioningOperationDto> context = futureTask.get(timeout, TimeUnit.MILLISECONDS);
//
return context.getContent();
} catch (InterruptedException ex) {
futureTask.cancel(true);
// propagate exception to upper catch
throw ex;
} catch (TimeoutException ex) {
futureTask.cancel(true);
// put thread into queue and wait => timeout too => retry mecchanism will work
throw new ResultCodeException(AccResultCode.PROVISIONING_TIMEOUT, ImmutableMap.of("name", provisioningOperation.getSystemEntityUid(), "system", provisioningOperation.getSystem(), "operationType", provisioningOperation.getOperationType(), "objectClass", provisioningOperation.getProvisioningContext().getConnectorObject().getObjectClass(), "timeout", String.valueOf(timeout)), ex);
}
} catch (Exception ex) {
return provisioningOperationService.handleFailed(provisioningOperation, ex);
} finally {
try {
UUID roleRequestId = provisioningOperation.getRoleRequestId();
if (roleRequestId != null) {
// Check of the state for whole request
// Create mock request -> we don't wont load request from DB -> optimization
IdmRoleRequestDto mockRequest = new IdmRoleRequestDto();
mockRequest.setId(roleRequestId);
mockRequest.setState(RoleRequestState.EXECUTED);
IdmRoleRequestDto returnedReqeust = roleRequestService.refreshSystemState(mockRequest);
OperationResultDto systemState = returnedReqeust.getSystemState();
if (systemState == null) {
// State on system of request was not changed (may be not all provisioning operations are
// resolved)
} else {
// We have final state on systems
IdmRoleRequestDto requestDto = roleRequestService.get(roleRequestId);
if (requestDto != null) {
requestDto.setSystemState(systemState);
roleRequestService.save(requestDto);
} else {
LOG.info(MessageFormat.format("Refresh role-request system state: Role-request with ID [{0}] was not found (maybe was deleted).", roleRequestId));
}
}
}
} catch (Exception ex) {
return provisioningOperationService.handleFailed(provisioningOperation, ex);
}
}
} finally {
UUID eventId = provisioningOperation.getManualEventId();
if (eventId != null) {
IdmEntityEventDto startEvent = entityEventManager.getEvent(eventId);
if (startEvent != null) {
// Complete a manual event (for ensure end of the sync).
entityEventManager.completeManualEvent(startEvent);
}
}
}
}
use of eu.bcvsolutions.idm.core.api.event.EventContext in project CzechIdMng by bcvsolutions.
the class DuplicateRoleLogProcessorIntegrationTest method testDuplicateLogIsCreated.
@Test
public void testDuplicateLogIsCreated() {
IdmRoleDto role = getHelper().createRole(null, null, getHelper().createName());
IdmRoleDto targetRole = new IdmRoleDto();
targetRole.setBaseCode(role.getBaseCode());
targetRole.setEnvironment(getHelper().createName());
//
// publish duplicate event
EntityEvent<IdmRoleDto> event = new RoleEvent(RoleEventType.DUPLICATE, targetRole);
// original source is the cloned role
event.setOriginalSource(role);
EventContext<IdmRoleDto> context = roleService.publish(event);
//
// processor conditional is not match
Assert.assertFalse(context.getResults().stream().anyMatch(r -> r.getProcessor().getClass().equals(DuplicateRoleLogProcessor.class)));
//
targetRole = new IdmRoleDto();
targetRole.setBaseCode(role.getBaseCode());
targetRole.setEnvironment(getHelper().createName());
Map<String, Serializable> props = new HashMap<>();
props.put(DuplicateRoleLogProcessor.PARAMETER_INCLUDE_LOG, true);
event = new RoleEvent(RoleEventType.DUPLICATE, targetRole, props);
// original source is the cloned role
event.setOriginalSource(role);
context = roleService.publish(event);
//
// processor will be evaluated
Assert.assertTrue(context.getResults().stream().anyMatch(r -> r.getProcessor().getClass().equals(DuplicateRoleLogProcessor.class)));
}
use of eu.bcvsolutions.idm.core.api.event.EventContext in project CzechIdMng by bcvsolutions.
the class IdentityContractUpdateByAutomaticRoleProcessor method process.
@Override
public EventResult<IdmIdentityContractDto> process(EntityEvent<IdmIdentityContractDto> event) {
IdmIdentityContractDto contract = event.getContent();
IdmIdentityContractDto previous = event.getOriginalSource();
UUID previousPosition = previous == null ? null : previous.getWorkPosition();
UUID newPosition = contract.getWorkPosition();
boolean validityChangedToValid = previous == null ? false : contract.isValidNowOrInFuture() && previous.isValidNowOrInFuture() != contract.isValidNowOrInFuture();
IdmRoleRequestDto roleRequest = new IdmRoleRequestDto();
// flag can be processed afterwards
if (getBooleanProperty(AutomaticRoleManager.SKIP_RECALCULATION, event.getProperties())) {
LOG.debug("Automatic roles are skipped for contract [{}], state [{}] " + "for position will be created only.", contract.getId(), CoreResultCode.AUTOMATIC_ROLE_SKIPPED.getCode());
//
Map<String, Serializable> properties = new HashMap<>();
// original contract as property
properties.put(EntityEvent.EVENT_PROPERTY_ORIGINAL_SOURCE, event.getOriginalSource());
entityStateManager.createState(contract, OperationState.BLOCKED, contract.isValidNowOrInFuture() ? CoreResultCode.AUTOMATIC_ROLE_SKIPPED : CoreResultCode.AUTOMATIC_ROLE_SKIPPED_INVALID_CONTRACT, properties);
//
return new DefaultEventResult<>(event, this);
}
if (!contract.isValidNowOrInFuture()) {
// but we need to add skipped flag above, even when invalid contract is updated
return new DefaultEventResult<>(event, this);
}
//
if (previous == null || !Objects.equals(newPosition, previousPosition) || validityChangedToValid) {
// work positions has some difference or validity changes
List<IdmIdentityRoleDto> assignedRoles = getAssignedAutomaticRoles(contract.getId());
// remove all automatic roles by attribute and by other contract position
if (!assignedRoles.isEmpty()) {
assignedRoles = assignedRoles.stream().filter(autoRole -> {
// remove automatic roles by attribute - solved by different process
AbstractIdmAutomaticRoleDto automaticRoleDto = DtoUtils.getEmbedded(autoRole, IdmIdentityRole_.automaticRole, (AbstractIdmAutomaticRoleDto) null);
if (automaticRoleDto instanceof IdmRoleTreeNodeDto) {
return true;
}
return false;
}).filter(identityRole -> {
// remove automatic roles by attribute - solved by different process
return identityRole.getContractPosition() == null;
}).collect(Collectors.toList());
}
//
Set<UUID> previousAutomaticRoles = assignedRoles.stream().filter(identityRole -> {
return identityRole.getAutomaticRole() != null;
}).map(identityRole -> {
return identityRole.getAutomaticRole();
}).collect(Collectors.toSet());
Set<IdmRoleTreeNodeDto> addedAutomaticRoles = new HashSet<>();
if (newPosition != null && contract.isValidNowOrInFuture()) {
addedAutomaticRoles = roleTreeNodeService.getAutomaticRolesByTreeNode(newPosition);
}
// prevent to remove newly added or still exists roles
Set<UUID> removedAutomaticRoles = new HashSet<>(previousAutomaticRoles);
removedAutomaticRoles.removeAll(addedAutomaticRoles.stream().map(IdmRoleTreeNodeDto::getId).collect(Collectors.toList()));
addedAutomaticRoles.removeIf(a -> {
return previousAutomaticRoles.contains(a.getId());
});
//
for (UUID removedAutomaticRole : removedAutomaticRoles) {
Iterator<IdmIdentityRoleDto> iter = assignedRoles.iterator();
while (iter.hasNext()) {
IdmIdentityRoleDto identityRole = iter.next();
if (Objects.equals(identityRole.getAutomaticRole(), removedAutomaticRole)) {
// check, if role will be added by new automatic roles and prevent removing
IdmRoleTreeNodeDto addedAutomaticRole = getByRole(identityRole.getRole(), addedAutomaticRoles);
if (addedAutomaticRole == null) {
// remove assigned role
IdmConceptRoleRequestDto conceptRoleRequest = new IdmConceptRoleRequestDto();
conceptRoleRequest.setIdentityRole(identityRole.getId());
conceptRoleRequest.setRole(identityRole.getRole());
conceptRoleRequest.setOperation(ConceptRoleRequestOperation.REMOVE);
//
roleRequest.getConceptRoles().add(conceptRoleRequest);
iter.remove();
} else {
// change relation only
IdmConceptRoleRequestDto conceptRoleRequest = new IdmConceptRoleRequestDto();
conceptRoleRequest.setIdentityRole(identityRole.getId());
conceptRoleRequest.setAutomaticRole(addedAutomaticRole.getId());
conceptRoleRequest.setIdentityContract(contract.getId());
conceptRoleRequest.setValidFrom(contract.getValidFrom());
conceptRoleRequest.setValidTill(contract.getValidTill());
conceptRoleRequest.setRole(identityRole.getRole());
conceptRoleRequest.setOperation(ConceptRoleRequestOperation.UPDATE);
//
roleRequest.getConceptRoles().add(conceptRoleRequest);
//
// new automatic role is not needed
addedAutomaticRoles.remove(addedAutomaticRole);
}
}
}
}
// change date - for unchanged assigned roles only
if (previous != null && EntityUtils.validableChanged(previous, contract)) {
roleRequest.getConceptRoles().addAll(changeValidable(contract, assignedRoles));
}
// add identity roles
for (AbstractIdmAutomaticRoleDto autoRole : addedAutomaticRoles) {
IdmConceptRoleRequestDto conceptRoleRequest = new IdmConceptRoleRequestDto();
conceptRoleRequest.setIdentityContract(contract.getId());
conceptRoleRequest.setValidFrom(contract.getValidFrom());
conceptRoleRequest.setValidTill(contract.getValidTill());
conceptRoleRequest.setRole(autoRole.getRole());
conceptRoleRequest.setAutomaticRole(autoRole.getId());
conceptRoleRequest.setOperation(ConceptRoleRequestOperation.ADD);
//
roleRequest.getConceptRoles().add(conceptRoleRequest);
}
// contract is enabled => process all contract positions
if (validityChangedToValid) {
IdmContractPositionFilter filter = new IdmContractPositionFilter();
filter.setIdentityContractId(contract.getId());
//
for (IdmContractPositionDto position : contractPositionService.find(filter, null).getContent()) {
CoreEvent<IdmContractPositionDto> positionEvent = new CoreEvent<>(CoreEventType.NOTIFY, position);
// we don't need the second asynchronicity
positionEvent.setPriority(PriorityType.IMMEDIATE);
positionEvent.getProperties().put(EVENT_PROPERTY_REQUEST, roleRequest);
// recount automatic roles for given position
EventContext<IdmContractPositionDto> context = contractPositionService.publish(positionEvent, event);
// get modified prepared request
if (context.getLastResult() != null) {
roleRequest = (IdmRoleRequestDto) context.getLastResult().getEvent().getProperties().get(EVENT_PROPERTY_REQUEST);
}
}
}
} else if (previous != null && EntityUtils.validableChanged(previous, contract)) {
// process validable change only
roleRequest.getConceptRoles().addAll(changeValidable(contract, getAssignedAutomaticRoles(contract.getId())));
}
// start request at end asynchronously
roleRequest.setApplicant(contract.getIdentity());
RoleRequestEvent requestEvent = new RoleRequestEvent(RoleRequestEventType.EXCECUTE, roleRequest);
roleRequestService.startConcepts(requestEvent, event);
//
return new DefaultEventResult<>(event, this);
}
use of eu.bcvsolutions.idm.core.api.event.EventContext in project CzechIdMng by bcvsolutions.
the class DefaultEntityEventManager method putToQueue.
/**
* Try put event to queue - event is put into queue, only if it's not executed synchronously.
* If event is executed synchronously, then {@link EventContext} is returned, {@code null} is returned otherwise.
*
* @param entityEvent
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private EventContext<?> putToQueue(IdmEntityEventDto entityEvent) {
if (entityEvent.getPriority() == PriorityType.IMMEDIATE) {
LOG.trace("Event type [{}] for owner with id [{}] will be executed synchronously.", entityEvent.getEventType(), entityEvent.getOwnerId());
// we don't persist events and their states
return process(new CoreEvent<>(EntityEventType.EXECUTE, entityEvent));
}
if (!eventConfiguration.isAsynchronous()) {
LOG.trace("Event type [{}] for owner with id [{}] will be executed synchronously, asynchronous event processing [{}] is disabled.", entityEvent.getEventType(), entityEvent.getOwnerId(), EventConfiguration.PROPERTY_EVENT_ASYNCHRONOUS_ENABLED);
// synchronous processing
return process(new CoreEvent<>(EntityEventType.EXECUTE, entityEvent));
}
//
// get enabled processors, which listen given event (conditional is evaluated)
final EntityEvent<?> event = toEvent(entityEvent);
List<EntityEventProcessor> listenProcessors = getEnabledProcessors(event).stream().filter(processor -> processor.conditional(event)).collect(Collectors.toList());
if (listenProcessors.isEmpty()) {
LOG.debug("Event type [{}] for owner with id [{}] will not be executed, no enabled processor is registered.", entityEvent.getEventType(), entityEvent.getOwnerId());
// return empty context - nothing is processed
return new DefaultEventContext<>();
}
//
// evaluate event priority by registered processors
PriorityType priority = evaluatePriority(event, listenProcessors);
if (priority != null && priority.getPriority() < entityEvent.getPriority().getPriority()) {
entityEvent.setPriority(priority);
}
// registered processors voted about event will be processed synchronously
if (entityEvent.getPriority() == PriorityType.IMMEDIATE) {
LOG.trace("Event type [{}] for owner with id [{}] will be executed synchronously.", entityEvent.getEventType(), entityEvent.getOwnerId());
// synchronous processing
// we don't persist events and their states
process(new CoreEvent<>(EntityEventType.EXECUTE, entityEvent));
}
//
// TODO: send notification only when event fails
// notification - info about registered (asynchronous) processors
// Map<String, Object> parameters = new LinkedHashMap<>();
// parameters.put("eventType", entityEvent.getEventType());
// parameters.put("ownerId", entityEvent.getOwnerId());
// parameters.put("instanceId", entityEvent.getInstanceId());
// parameters.put("processors", registeredProcessors
// .stream()
// .map(DefaultEntityEventManager.this::toDto)
// .collect(Collectors.toList()));
// notificationManager.send(
// CoreModuleDescriptor.TOPIC_EVENT,
// new IdmMessageDto
// .Builder()
// .setLevel(NotificationLevel.INFO)
// .setModel(new DefaultResultModel(CoreResultCode.EVENT_ACCEPTED, parameters))
// .build());
//
// persist event - asynchronous processing
entityEvent = entityEventService.save(entityEvent);
addEventCache(entityEvent.getId(), entityEvent.getTransactionId());
// not processed - persisted into queue
return null;
}
use of eu.bcvsolutions.idm.core.api.event.EventContext in project CzechIdMng by bcvsolutions.
the class DuplicateRoleCompositionProcessor method process.
@Override
@SuppressWarnings("unchecked")
public EventResult<IdmRoleDto> process(EntityEvent<IdmRoleDto> event) {
IdmRoleDto cloned = event.getContent();
IdmRoleDto originalSource = event.getOriginalSource();
//
Map<String, Serializable> props = resolveProperties(event);
Set<UUID> processedRoles = (Set<UUID>) props.get(RoleEvent.PROPERTY_PROCESSED_ROLES);
processedRoles.add(cloned.getId());
//
// find and clone business role composition
// clone roles recursively
Set<String> processedSubRoles = new HashSet<>();
Map<String, IdmRoleCompositionDto> currentSubRoles = new HashMap<>();
roleCompositionService.findDirectSubRoles(cloned.getId()).forEach(composition -> {
IdmRoleDto subRole = DtoUtils.getEmbedded(composition, IdmRoleComposition_.sub);
currentSubRoles.put(subRole.getCode(), composition);
});
//
roleCompositionService.findDirectSubRoles(originalSource.getId()).stream().filter(composition -> {
return includeComposition(event, composition);
}).forEach(composition -> {
// find sub role on the target environment
IdmRoleDto subRole = DtoUtils.getEmbedded(composition, IdmRoleComposition_.sub);
IdmRoleDto targetRole = roleService.getByBaseCodeAndEnvironment(subRole.getBaseCode(), cloned.getEnvironment());
//
if (targetRole != null || duplicateRecursively(event, subRole, targetRole)) {
if (targetRole == null) {
// new clone
targetRole = prepareRole(subRole.getBaseCode(), cloned.getEnvironment());
}
if (targetRole != null && subRole.getId().equals(targetRole.getId())) {
LOG.debug("Role [{}] is duplicated on the same environment - skipping recursion for the same roles", targetRole.getCode());
} else if (targetRole != null && processedRoles.contains(targetRole.getId())) {
LOG.debug("Role [{}] was already processed by other business role composition - cycle, skipping", targetRole.getCode());
} else {
//
// clone / update
EntityEvent<IdmRoleDto> subEvent = new RoleEvent(RoleEventType.DUPLICATE, targetRole, props);
// original source is the cloned role
subEvent.setOriginalSource(subRole);
// we want to be sync
subEvent.setPriority(PriorityType.IMMEDIATE);
EventContext<IdmRoleDto> resultSubRole = roleService.publish(subEvent, event);
targetRole = resultSubRole.getContent();
}
//
// create the composition (or check composition exists)
// find exists
processedSubRoles.add(targetRole.getCode());
if (!currentSubRoles.containsKey(targetRole.getCode())) {
IdmRoleCompositionDto cloneComposition = new IdmRoleCompositionDto(cloned.getId(), targetRole.getId());
EntityEvent<IdmRoleCompositionDto> createCompositionEvent = new RoleCompositionEvent(RoleCompositionEventType.CREATE, cloneComposition);
// we want to be sync
createCompositionEvent.setPriority(PriorityType.IMMEDIATE);
roleCompositionService.publish(createCompositionEvent, event);
}
}
});
//
// remove unprocessed sub roles, which was removed in surce role
currentSubRoles.entrySet().stream().filter(entry -> {
return !processedSubRoles.contains(entry.getKey());
}).filter(entry -> {
return includeComposition(event, entry.getValue());
}).forEach(entry -> {
// dirty flag role composition only - will be processed after parent action ends
IdmEntityStateDto stateDeleted = new IdmEntityStateDto();
stateDeleted.setEvent(event.getId());
stateDeleted.setSuperOwnerId(cloned.getId());
stateDeleted.setResult(new OperationResultDto.Builder(OperationState.RUNNING).setModel(new DefaultResultModel(CoreResultCode.DELETED)).build());
entityStateManager.saveState(entry.getValue(), stateDeleted);
});
//
return new DefaultEventResult<>(event, this);
}
Aggregations