Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AUP and user's lifecycle missing email notifications #787

Merged
merged 1 commit into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public class AupSignatureController {
private final IamAupRepository aupRepo;
private final TimeProvider timeProvider;
private final ApplicationEventPublisher eventPublisher;

public AupSignatureController(AupSignatureConverter conv, AccountUtils utils,
IamAupSignatureRepository signatureRepo, IamAupRepository aupRepo, TimeProvider timeProvider,
ApplicationEventPublisher publisher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public IamAup updateAup(AupDTO aupDto) {
aup.setDescription(aupDto.getDescription());
aup.setUrl(aupDto.getUrl());
aup.setSignatureValidityInDays(aupDto.getSignatureValidityInDays());
aup.setAupRemindersInDays(aupDto.getAupRemindersInDays());
/*
* Due to transition from text to URL, when updating the AUP only URL is considered while text
* is ignored and set to null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ public IamAup entityFromDto(AupDTO dto) {
aup.setSignatureValidityInDays(dto.getSignatureValidityInDays());
aup.setUrl(dto.getUrl());
aup.setText(dto.getText());
aup.setAupRemindersInDays(dto.getAupRemindersInDays());
return aup;
}

@Override
public AupDTO dtoFromEntity(IamAup entity) {
return new AupDTO(entity.getUrl(), entity.getText(), entity.getDescription(),
entity.getSignatureValidityInDays(), entity.getCreationTime(), entity.getLastUpdateTime());
entity.getSignatureValidityInDays(), entity.getCreationTime(), entity.getLastUpdateTime(),
entity.getAupRemindersInDays());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@

import java.util.Date;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.URL;
Expand All @@ -30,6 +28,7 @@
import it.infn.mw.iam.api.scim.controller.utils.JsonDateSerializer;
import it.infn.mw.iam.api.validators.NoQueryParamsUrl;

@AupRemindersAndSignature
public class AupDTO {

@NotBlank(message = "Invalid AUP: the AUP URL cannot be blank")
Expand All @@ -43,10 +42,10 @@ public class AupDTO {
message = "Invalid AUP: the description string must be at most 128 characters long")
String description;

@NotNull(message = "Invalid AUP: signatureValidityInDays is required")
@Min(value = 0L, message = "Invalid AUP: signatureValidityInDays must be >= 0")
Long signatureValidityInDays;

String aupRemindersInDays = "30,15,1";

@JsonSerialize(using = JsonDateSerializer.class)
Date creationTime;

Expand All @@ -57,13 +56,15 @@ public AupDTO(@JsonProperty("url") String url, @JsonProperty("text") String text
@JsonProperty("description") String description,
@JsonProperty("signatureValidityInDays") Long signatureValidityInDays,
@JsonProperty("creationTime") Date creationTime,
@JsonProperty("lastUpdateTime") Date lastUpdateTime) {
@JsonProperty("lastUpdateTime") Date lastUpdateTime,
@JsonProperty("aupRemindersInDays") String aupRemindersInDays) {
this.url = url;
this.description = description;
this.signatureValidityInDays = signatureValidityInDays;
this.creationTime = creationTime;
this.lastUpdateTime = lastUpdateTime;
this.text = text;
this.aupRemindersInDays = aupRemindersInDays;
}

public String getDescription() {
Expand Down Expand Up @@ -101,6 +102,14 @@ public void setSignatureValidityInDays(Long signatureValidityInDays) {
this.signatureValidityInDays = signatureValidityInDays;
}

public String getAupRemindersInDays() {
return aupRemindersInDays;
}

public void setAupRemindersInDays(String aupRemindersInDays) {
this.aupRemindersInDays = aupRemindersInDays;
}


public Date getCreationTime() {
return creationTime;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.aup.model;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Retention(RUNTIME)
@Target({TYPE})
@Constraint(validatedBy = AupRemindersAndSignatureValidator.class)
public @interface AupRemindersAndSignature {

String message() default "Invalid AUP";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.aup.model;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class AupRemindersAndSignatureValidator implements ConstraintValidator<AupRemindersAndSignature, AupDTO> {

@Override
public boolean isValid(AupDTO value, ConstraintValidatorContext context) {

Long signatureValidityInDays = value.getSignatureValidityInDays();
String aupRemindersInDays = value.getAupRemindersInDays();

if (signatureValidityInDays == null) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate("Invalid AUP: signatureValidityInDays is required")
.addPropertyNode("signatureValidityInDays")
.addConstraintViolation();
return false;
}

if (signatureValidityInDays < 0) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate("Invalid AUP: signatureValidityInDays must be >= 0")
.addPropertyNode("signatureValidityInDays")
.addConstraintViolation();
return false;
}

if (aupRemindersInDays == null || aupRemindersInDays.isEmpty()) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(
"Invalid AUP: aupRemindersInDays cannot be empty or null")
.addConstraintViolation();
return false;
}

try {
List<Integer> numbers = Arrays.stream(aupRemindersInDays.split(","))
.map(String::trim)
.map(Integer::parseInt)
.collect(Collectors.toList());

if (numbers.stream().anyMatch(i -> i <= 0)) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(
"Invalid AUP: zero or negative values for reminders are not allowed")
.addConstraintViolation();
return false;
}

if (numbers.stream().anyMatch(i -> i >= signatureValidityInDays)) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(
"Invalid AUP: aupRemindersInDays must be smaller than signatureValidityInDays")
.addConstraintViolation();
return false;
}

Set<Integer> uniqueNumbers = new HashSet<>(numbers);
if (uniqueNumbers.size() != numbers.size()) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate("Invalid AUP: duplicate values for reminders are not allowed")
.addConstraintViolation();
return false;
}

return true;
} catch (NumberFormatException e) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Invalid AUP: non-integer value found")
.addConstraintViolation();
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import it.infn.mw.iam.core.user.IamAccountService;
import it.infn.mw.iam.core.user.exception.CredentialAlreadyBoundException;
import it.infn.mw.iam.core.user.exception.UserAlreadyExistsException;
import it.infn.mw.iam.notification.NotificationFactory;
import it.infn.mw.iam.persistence.model.IamAccount;
import it.infn.mw.iam.persistence.repository.IamAccountRepository;
import it.infn.mw.iam.registration.validation.UsernameValidator;
Expand All @@ -84,19 +85,21 @@ public class ScimUserProvisioning
private final IamAccountRepository accountRepository;
private final UserConverter userConverter;
private final DefaultAccountUpdaterFactory updatersFactory;
private final NotificationFactory notificationFactory;

private ApplicationEventPublisher eventPublisher;

public ScimUserProvisioning(IamAccountService accountService,
OAuth2TokenEntityService tokenService, IamAccountRepository accountRepository,
PasswordEncoder passwordEncoder, UserConverter userConverter, OidcIdConverter oidcIdConverter,
SamlIdConverter samlIdConverter, SshKeyConverter sshKeyConverter,
X509CertificateConverter x509CertificateConverter,
UsernameValidator usernameValidator) {
X509CertificateConverter x509CertificateConverter, UsernameValidator usernameValidator,
NotificationFactory notificationFactory) {

this.accountService = accountService;
this.accountRepository = accountRepository;
this.userConverter = userConverter;
this.notificationFactory = notificationFactory;
this.updatersFactory = new DefaultAccountUpdaterFactory(passwordEncoder, accountRepository,
accountService, tokenService, oidcIdConverter, samlIdConverter, sshKeyConverter,
x509CertificateConverter, usernameValidator);
Expand Down Expand Up @@ -266,11 +269,22 @@ private void executePatchOperation(IamAccount account, ScimPatchOperation<ScimUs
accountRepository.save(account);
for (AccountUpdater u : updatesToPublish) {
u.publishUpdateEvent(this, eventPublisher);
handleSpecificUpdateType(account, u);
}

}
}

private void handleSpecificUpdateType(IamAccount account, AccountUpdater u) {
if (ACCOUNT_REPLACE_ACTIVE.equals(u.getType())) {
if (account.isActive()) {
notificationFactory.createAccountRestoredMessage(account);
} else {
notificationFactory.createAccountSuspendedMessage(account);
}
}
}

@Override
public void update(final String id, final List<ScimPatchOperation<ScimUser>> operations) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import it.infn.mw.iam.config.lifecycle.LifecycleProperties;
import it.infn.mw.iam.core.lifecycle.ExpiredAccountsHandler;
import it.infn.mw.iam.core.user.IamAccountService;
import it.infn.mw.iam.core.web.aup.AupReminderTask;
import it.infn.mw.iam.core.web.wellknown.IamWellKnownInfoProvider;
import it.infn.mw.iam.notification.NotificationDelivery;
import it.infn.mw.iam.notification.NotificationDeliveryTask;
Expand Down Expand Up @@ -84,6 +85,9 @@ public class TaskConfig implements SchedulingConfigurer {
@Autowired
ExpiredAccountsHandler expiredAccountsHandler;

@Autowired
AupReminderTask aupReminderTask;

@Autowired
CacheManager cacheManager;

Expand Down Expand Up @@ -127,6 +131,11 @@ public void clearExpiredDeviceCodes() {
deviceCodeService.clearExpiredDeviceCodes();
}

@Scheduled(fixedRateString = "${task.aupReminder:14400}", timeUnit = TimeUnit.SECONDS)
public void scheduledAupRemindersTask() {
aupReminderTask.sendAupReminders();
}

public void schedulePendingNotificationsDelivery(final ScheduledTaskRegistrar taskRegistrar) {

if (notificationTaskPeriodMsec < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import it.infn.mw.iam.core.user.exception.CredentialAlreadyBoundException;
import it.infn.mw.iam.core.user.exception.InvalidCredentialException;
import it.infn.mw.iam.core.user.exception.UserAlreadyExistsException;
import it.infn.mw.iam.notification.NotificationFactory;
import it.infn.mw.iam.persistence.model.IamAccount;
import it.infn.mw.iam.persistence.model.IamAccountGroupMembership;
import it.infn.mw.iam.persistence.model.IamAttribute;
Expand Down Expand Up @@ -81,11 +82,13 @@ public class DefaultIamAccountService implements IamAccountService, ApplicationE
private ApplicationEventPublisher eventPublisher;
private final OAuth2TokenEntityService tokenService;
private final IamAccountClientRepository accountClientRepo;
private final NotificationFactory notificationFactory;

public DefaultIamAccountService(Clock clock, IamAccountRepository accountRepo,
IamGroupRepository groupRepo, IamAuthoritiesRepository authoritiesRepo,
PasswordEncoder passwordEncoder, ApplicationEventPublisher eventPublisher,
OAuth2TokenEntityService tokenService, IamAccountClientRepository accountClientRepo) {
OAuth2TokenEntityService tokenService, IamAccountClientRepository accountClientRepo,
NotificationFactory notificationFactory) {

this.clock = clock;
this.accountRepo = accountRepo;
Expand All @@ -95,6 +98,7 @@ public DefaultIamAccountService(Clock clock, IamAccountRepository accountRepo,
this.eventPublisher = eventPublisher;
this.tokenService = tokenService;
this.accountClientRepo = accountClientRepo;
this.notificationFactory = notificationFactory;
}

private void labelSetEvent(IamAccount account, IamLabel label) {
Expand Down Expand Up @@ -403,6 +407,7 @@ public IamAccount disableAccount(IamAccount account) {
account.touch();
accountRepo.save(account);
eventPublisher.publishEvent(new AccountDisabledEvent(this, account));
notificationFactory.createAccountSuspendedMessage(account);
return account;
}

Expand All @@ -412,6 +417,7 @@ public IamAccount restoreAccount(IamAccount account) {
account.touch();
accountRepo.save(account);
eventPublisher.publishEvent(new AccountRestoredEvent(this, account));
notificationFactory.createAccountRestoredMessage(account);
return account;
}

Expand Down
Loading
Loading